Saltar al contenido

Desarrolladores

Construye sobre tus datos de calibración.

La API pública de Axiospec es una interfaz REST a tus instrumentos y a tus registros de calibración. Lee y crea instrumentos, registra calibraciones en el libro con detección de manipulaciones y trae tus sedes y tus normas a tus propios sistemas. Autentícate con una clave de API del espacio de trabajo y listo.

Primeros pasos

Todo lo que necesitas antes de tu primera llamada

Lee esto una vez y luego abre la referencia interactiva completa para ver la forma exacta de la petición y de la respuesta de cada endpoint.

Qué hace la API

La API pública de Axiospec es una interfaz REST a tu programa de calibración. Lee y crea instrumentos, registra calibraciones en el libro con detección de manipulaciones y lee las sedes de tu espacio de trabajo y las normas de cumplimiento que has seleccionado.

Es un contrato estable y cuidado, separado de los endpoints internos que usan la aplicación web y la móvil, así que tu integración sigue funcionando a medida que el producto evoluciona. Todas las respuestas son JSON.

URL base

Todos los endpoints cuelgan de una sola URL base. Cada ruta de la referencia de abajo es relativa a ella.

https://axiospec.com/api/public/v1

Autenticación

Autentica cada petición con una clave de API por espacio de trabajo. Un administrador del espacio de trabajo crea una en la aplicación, en Settings y luego API Keys. Las claves se muestran una sola vez al crearlas y llevan el prefijo ctk_. Guarda la clave como un secreto y no la publiques nunca en código de cliente.

Envía la clave en cada petición como token bearer:

Authorization: Bearer ctk_your_api_key

También se acepta la cabecera estándar X-API-Key si la prefieres: X-API-Key: ctk_tu_clave_de_api. Una petición sin clave devuelve 401.

Plan necesario

La API está disponible a partir del plan Professional. Una clave de un espacio de trabajo en el plan Free o Starter recibe un 403 con el código API_ACCESS_TIER_REQUIRED. Mejora el plan del espacio de trabajo para activarla.

Ámbitos

Cada clave se emite con un ámbito. Una clave de lectura puede listar y consultar. Una clave de escritura además puede crear instrumentos, actualizarlos y registrar calibraciones (escritura siempre implica lectura).

Una clave de solo lectura que intente escribir recibe un 403 con el código INSUFFICIENT_SCOPE, que nombra el ámbito necesario. Emite claves de solo lectura para las integraciones de informes, así nunca pueden cambiar un registro.

Límites de peticiones y sus cabeceras

Las peticiones están limitadas a 120 por minuto, contadas por clave de API y no por IP, así que una integración no puede ahogar a otra y muchas claves detrás de una misma red de oficina no se limitan juntas.

Cada respuesta de la API lleva la ventana actual en cabeceras, así que puedes marcar tu propio ritmo sin adivinar. X-RateLimit-Limit es el techo (120), X-RateLimit-Remaining es cuántas peticiones quedan en la ventana actual y X-RateLimit-Reset son los segundos que faltan para que la ventana se reinicie y Remaining vuelva al límite completo.

Superar el límite devuelve un 429 con el código RATE_LIMITED. En el 429, una cabecera Retry-After (en segundos) te dice exactamente cuánto esperar. Respétala y reintenta. Leer estas cabeceras en vez de fijar una espera en el código te mantiene rápido cuando hay margen y prudente cuando no lo hay.

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 41
Retry-After: 41   (present only on a 429)

Sincronización incremental

Para mantener un sistema externo al día sin releerlo todo, trae solo lo que ha cambiado desde tu última ejecución. Cada colección acepta un parámetro updated_since (una marca de tiempo ISO-8601 en UTC) que devuelve solo los registros modificados en ese instante o después, más un parámetro sort para recorrerlos del más antiguo al más nuevo e ir avanzando una marca de avance.

El flujo de calibraciones de todo el espacio de trabajo, GET /calibrations, está hecho justo para esto: devuelve todas las calibraciones de todos tus instrumentos en un solo flujo paginado, así no tienes que recorrer instrumento por instrumento. Ordena de forma ascendente por updated_at, pagina y guarda el created_at del último registro que viste. El libro es de solo anexado, así que un registro de calibración nunca cambia después de escribirse y su created_at es su hora de última modificación. sort=updated_at apunta a ese mismo instante.

En la siguiente ejecución, pasa ese valor guardado como updated_since. Solapa el límite un segundo o dos y descarta duplicados por el id del registro, por si hay desfase de reloj. Guarda la marca de avance solo después de haber procesado la página de forma duradera.

Los instrumentos admiten los mismos parámetros updated_since y sort (GET /instruments), que filtran por la hora de última modificación del instrumento. Las filas del listado no incluyen un campo de marca de tiempo, así que para instrumentos usa como siguiente updated_since la hora de reloj que capturaste justo antes de la petición. El GET /instruments/{id} de un solo instrumento devuelve created_at y updated_at si los necesitas.

# First run: no watermark, oldest-first, page through.
GET /api/public/v1/calibrations?sort=updated_at&limit=100

# Save the created_at of the LAST record you processed, e.g.
#   watermark = "2026-07-09T15:30:00Z"

# Next run: only what is new since the watermark.
GET /api/public/v1/calibrations?updated_since=2026-07-09T15:30:00Z&sort=updated_at&limit=100

Filtrar instrumentos

GET /instruments acepta filtros para que traigas una porción exacta en vez de paginar la lista entera. asset_tag y serial_number coinciden con un valor exacto (útil para cuadrar un instrumento concreto con un registro del ERP). status filtra por estado del ciclo de vida, por ejemplo active o retired. site_id acota a una sede.

Dos filtros se derivan del estado de calibración. compliance_status filtra por el valor calculado, uno de COMPLIANT, WARNING, NON_COMPLIANT o NOT_CALIBRATED. next_due_before toma una fecha (YYYY-MM-DD) y devuelve los instrumentos cuya próxima calibración vence antes de ella, que es la consulta detrás de una lista de próximos vencimientos o de vencidos. Los filtros se combinan, así que puedes pedir los instrumentos activos y no conformes de una sede en una sola llamada.

# Everything overdue or due before a date, oldest instruments first:
GET /api/public/v1/instruments?compliance_status=NON_COMPLIANT&next_due_before=2026-08-01

# Reconcile one instrument by its asset tag:
GET /api/public/v1/instruments?asset_tag=MM-0042

Idempotencia

Registrar una calibración es la única escritura que nunca debe duplicarse: el libro es de solo anexado, así que no hay forma de deshacer un envío doble. Por eso POST /instruments/{id}/calibrations exige una cabecera Idempotency-Key (cualquier cadena única que generes, por ejemplo un UUID). Es obligatoria, no opcional.

Si una petición se interrumpe y la reintentas con la misma clave, la API devuelve el registro que ya escribió en vez de escribir un segundo. Si falta la clave, devuelve un 400 con el código IDEMPOTENCY_KEY_REQUIRED. Genera una clave nueva por cada calibración que vayas a registrar.

Crear un instrumento (POST /instruments) también respeta una Idempotency-Key, pero aquí es opcional. Si la envías, un reintento con la misma clave devuelve el instrumento que creó la primera llamada en vez de un duplicado, igual que en el registro de calibraciones. La única diferencia es que la clave no es obligatoria. Si prefieres no gestionar claves para las creaciones, puedes descartar duplicados por tu lado usando el asset_tag o el serial_number del instrumento, que son únicos dentro de un espacio de trabajo, antes de hacer el POST.

Idempotency-Key: 6f9619ff-8b86-d011-b42d-00cf4fc964ff

Certificados

Cada calibración aprobada tiene un certificado PDF con tu marca. Consíguelo con GET /calibrations/{calibration_id}/certificate. La respuesta es el propio PDF (Content-Type application/pdf) como adjunto, idéntico byte a byte al certificado que genera la aplicación, así que puedes archivarlo o adjuntarlo a una orden de trabajo.

Un certificado solo existe para una calibración aprobada y vigente. Si el registro está anulado, sustituido por una entrada posterior o no es certificable por otro motivo, la petición devuelve un 404 con el código CERTIFICATE_UNAVAILABLE. Un id de calibración que no sea tuyo, o que no exista, devuelve un 404 simple que no revela nada sobre él.

curl "https://axiospec.com/api/public/v1/calibrations/CALIBRATION_ID/certificate" \
  -H "Authorization: Bearer ctk_your_api_key" \
  -o certificate.pdf

Dar de baja un instrumento

Cuando un instrumento sale de servicio, dalo de baja con POST /instruments/{id}/retire (una llamada con ámbito de escritura). Es una baja lógica: el status del instrumento pasa a retired y sale de la lista de activos por defecto, pero no se borra nada y su historial de calibración en el libro queda intacto para la auditoría. En la API no hay borrado definitivo.

La llamada devuelve el instrumento actualizado. Es idempotente: dar de baja un instrumento que ya está de baja no hace nada y devuelve el mismo registro, así que reintentar siempre es seguro. Dar de baja exige una clave de administrador o de responsable. Una clave de solo lectura o de técnico recibe un 403.

curl -X POST "https://axiospec.com/api/public/v1/instruments/INSTRUMENT_ID/retire" \
  -H "Authorization: Bearer ctk_your_api_key"

Marcas de tiempo y zonas horarias

Toda marca de tiempo que devuelve la API es ISO-8601 en UTC y termina en Z, por ejemplo 2026-07-09T15:30:00Z. Envía las marcas de tiempo igual. No hay respuestas con desfase ni en hora local que haya que normalizar.

Un campo es una fecha simple, no una marca de tiempo: la fecha de vencimiento de calibración de un instrumento. Los vencimientos se calculan en la zona horaria configurada en tu espacio de trabajo, así que un vencimiento es el día natural en que vence allí, y el filtro next_due_before toma una fecha (YYYY-MM-DD) y no una marca de tiempo. Si tus sistemas corren en otra zona, compara por la fecha y no por un instante de medianoche UTC.

El envoltorio de error

Todos los errores, en todos los endpoints, tienen la misma forma JSON: un code legible por máquina, un message legible por personas y, en algunos errores, un objeto details con lo concreto. Ramifica por code, nunca por el texto del mensaje, que se puede reescribir. El estado HTTP sigue teniendo significado (401 frente a 403 frente a 404), así que úsalo también.

Registrar una calibración también aplica los campos obligatorios de las normas que has seleccionado en tu espacio de trabajo. Si un campo obligatorio está vacío, la petición devuelve un 422 con el código FIELD_REQUIREMENTS_UNMET y un array missing_fields, donde cada entrada nombra el campo y la norma que lo exige, para que puedas pedir exactamente lo que falta.

{
  "code": "FIELD_REQUIREMENTS_UNMET",
  "message": "This calibration is missing fields your workspace's selected standard(s) require: measurement_uncertainty, decision_rule.",
  "details": {
    "missing_fields": [
      { "field": "measurement_uncertainty", "required_by": ["ISO/IEC 17025"] },
      { "field": "decision_rule", "required_by": ["ISO/IEC 17025"] }
    ]
  }
}

Webhooks

En vez de consultar la API cada cierto tiempo para descubrir qué ha cambiado, suscribe un endpoint webhook una vez y Axiospec entrega cada evento a tu URL según ocurre. Ganas latencia y ahorras mucho tráfico frente a releer colecciones que ya has visto, y nunca se te escapa un cambio entre dos consultas.

Suscríbete con POST /webhooks, pasando una url y, si quieres, una lista de tipos de evento. Omite el campo events para recibir todos los eventos (el catálogo completo está abajo). La respuesta devuelve el secreto de firma del endpoint exactamente una vez y nunca más, así que cópialo directamente a tu almacén de secretos. Gestionar webhooks necesita una clave con ámbito de escritura de administrador o de responsable, porque el endpoint recibe los datos de calibración e instrumentos de tu espacio de trabajo.

curl -X POST "https://axiospec.com/api/public/v1/webhooks" \
  -H "Authorization: Bearer ctk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/axiospec",
    "events": ["calibration.approved", "calibration.overdue"],
    "description": "Sync approvals into our QMS"
  }'

Cada evento se entrega como un POST HTTP cuyo cuerpo JSON es un envoltorio fijo: { "id", "type", "created_at", "data" }. El id es el identificador estable del evento, y se envía también en la cabecera Axiospec-Event-Id junto a Axiospec-Event-Type, Axiospec-Webhook-Id y Axiospec-Delivery-Attempt. La entrega es al menos una vez, así que el mismo evento puede llegar más de una vez (por ejemplo tras un reintento). Descarta duplicados por el id del envoltorio.

Una entrega cuenta como fallida en cualquier respuesta que no sea 2xx, y eso incluye una redirección 3xx: una redirección podría apuntar a una dirección interna, así que nunca se sigue. Los errores de transporte y los tiempos de espera agotados también cuentan como fallos. Las entregas fallidas se reintentan con un retroceso exponencial que abarca unos tres días, tras los cuales la entrega se marca como agotada. Un endpoint cuyas entregas recientes se agotan todas se desactiva solo, para que una URL muerta u hostil deje de consumir capacidad. Los endpoints deben ser HTTPS, y una URL que resuelva a una dirección privada o interna se rechaza al suscribirla.

Revisa lo que se envió con GET /webhooks/{id}/deliveries, que pagina el registro de entregas de un endpoint y acepta un filtro status (pending, failed, succeeded, exhausted). Para repetir una entrega, usa POST /webhooks/{id}/deliveries/{delivery_id}/retry: devuelve esa entrega a pending con fecha de ahora, así el siguiente envío la reenvía, con un horizonte de reintentos nuevo. Rota el secreto de un endpoint con POST /webhooks/{id}/rotate-secret (el secreto antiguo deja de verificar al instante) y detén las entregas con DELETE /webhooks/{id}.

Catálogo de eventos de webhook

Estos son los tipos de evento a los que puede suscribirse un webhook. Enumera los que quieras al suscribirte, u omite el campo events para recibirlos todos. El mismo catálogo está disponible en GET /webhooks/events para descubrirlo desde el código.

calibration.created
calibration.approved
calibration.rejected
calibration.corrected
calibration.voided
calibration.due_soon
calibration.overdue
instrument.created
instrument.updated
instrument.retired
instrument.status_changed

Verificar las firmas de webhook

Cada entrega lleva una cabecera Axiospec-Signature con la forma t=<segundos-unix>,v1=<hex>. Verifícala antes de confiar en un cuerpo: una firma válida demuestra que la petición vino de Axiospec y que el cuerpo no se alteró por el camino.

Lee la cabecera Axiospec-Signature y pártela por la coma en su parte t= (una marca de tiempo Unix en segundos) y su parte v1= (un HMAC hexadecimal en minúsculas). Recalcula HMAC-SHA256, con el secreto de firma de tu endpoint como clave, sobre la cadena formada por la marca de tiempo, un punto literal y el cuerpo crudo exacto de la petición, es decir f"{t}.{raw_body}". Compara tu resumen hexadecimal con el valor v1 mediante una comparación en tiempo constante, nunca con una igualdad normal.

Firma los bytes crudos tal como llegaron, antes de parsear el JSON o volver a serializarlo, para que tu entrada coincida con lo que se firmó. Rechaza la entrega si los resúmenes no coinciden, o si t tiene más de unos cinco minutos, lo que acota cuánto tiempo podría repetirse contra ti una petición capturada.

# Axiospec-Signature: t=1720625400,v1=3f6a9c...e1
t, v1    = split the header on "," then read the "t=" and "v1=" values
signed   = t + "." + raw_request_body        # the exact bytes received
expected = hex(hmac_sha256(secret, signed))  # lowercase hex digest

if not constant_time_equals(expected, v1):
    reject        # signature mismatch, do not trust the payload
if now_unix_seconds() - int(t) > 300:
    reject        # older than ~5 minutes, treat as a possible replay

accept            # then dedupe on the envelope id (Axiospec-Event-Id)

Pruébalo: dos ejemplos

Sustituye ctk_your_api_key por tu clave y INSTRUMENT_ID por un id de instrumento de la llamada de listado.

1. Listar instrumentos

curl "https://axiospec.com/api/public/v1/instruments?status=active&limit=25" \
  -H "Authorization: Bearer ctk_your_api_key"

2. Registrar una calibración

Fíjate en la cabecera Idempotency-Key obligatoria. Reintentar con la misma clave devuelve el registro que ya se escribió en vez de registrar un duplicado.

curl -X POST "https://axiospec.com/api/public/v1/instruments/INSTRUMENT_ID/calibrations" \
  -H "Authorization: Bearer ctk_your_api_key" \
  -H "Idempotency-Key: 6f9619ff-8b86-d011-b42d-00cf4fc964ff" \
  -H "Content-Type: application/json" \
  -d '{
    "result": "PASS",
    "performed_at": "2026-07-09T15:30:00Z",
    "nominal_value": "10.00 V",
    "tolerance": "±0.1%",
    "as_found_reading": "10.01 V",
    "as_left_reading": "10.00 V",
    "certificate_number": "CERT-2026-0142"
  }'

Códigos de error

Todos los códigos que devuelve la API, su estado HTTP y qué significan. Ramifica por el código.

Código HTTP Significado
UNAUTHORIZED 401 No hay clave de API, o la clave no es válida, está revocada o ha caducado.
API_ACCESS_TIER_REQUIRED 403 El espacio de trabajo está en el plan Free o Starter. La API necesita Professional o superior.
INSUFFICIENT_SCOPE 403 Una clave de solo lectura intentó escribir. Emite una clave con ámbito de escritura.
ACCESS_ENDED 403 La clave pertenece a un auditor cuya fecha de fin del acceso ya pasó. Un administrador del espacio de trabajo puede cambiar o quitar la fecha de fin.
FORBIDDEN 403 La clave es válida, pero su rol no permite la acción, por ejemplo una clave sin rol de responsable dando de baja un instrumento.
INSUFFICIENT_ROLE 403 La acción necesita rol de administrador o de responsable y el rol de la clave es menor, por ejemplo una clave de técnico gestionando un webhook.
NOT_FOUND 404 El recurso no existe, o queda fuera del alcance de espacio de trabajo o de sede de esta clave. Se devuelve igual en ambos casos para que no se filtre nada.
CERTIFICATE_UNAVAILABLE 404 La calibración existe pero no es certificable (no está aprobada, está anulada o está sustituida).
IDEMPOTENCY_KEY_REQUIRED 400 Se envió un registro de calibración sin la cabecera Idempotency-Key obligatoria.
INVALID_REQUEST 400 o 422 Una petición mal formada o que no pasó una validación con código, por ejemplo un token de consulta incorrecto, una clave de documento inservible o un campo de webhook no válido. La validación de documentos y webhooks devuelve 422; una petición incorrecta genérica devuelve 400. Ramifica por el código, el estado es secundario.
VALIDATION_ERROR 422 Uno o más campos no pasaron la validación. details.errors enumera cada campo y el motivo.
FIELD_REQUIREMENTS_UNMET 422 A una calibración le faltaba un campo que exigen las normas que has seleccionado. details.missing_fields los enumera.
INVALID_WEBHOOK_URL 422 La url del webhook no sirve: debe ser HTTPS, y se rechaza una URL que resuelva a una dirección privada o interna.
CONFLICT 409 La petición entra en conflicto con el estado actual del recurso.
WEBHOOK_LIMIT_REACHED 409 El espacio de trabajo ya tiene el máximo de endpoints de webhook. Borra uno antes de añadir otro.
DELIVERY_CONFLICT 409 Una entrega de webhook no se puede reintentar en su estado actual, por ejemplo repetir una entrega que aún no está resuelta.
OBJECT_NOT_UPLOADED 409 Se registró un documento con una clave cuyo archivo nunca se subió. Sube el archivo a la URL prefirmada primero y regístralo después.
METHOD_NOT_ALLOWED 405 Ese método HTTP no está soportado en esta ruta.
RATE_LIMITED 429 Se superó el límite de 120 por minuto. Espera los segundos de Retry-After y reintenta.
INTERNAL_ERROR 500 Un error inesperado del servidor. Una lectura se puede reintentar sin problema; un registro de calibración, reinténtalo con la misma Idempotency-Key.

Versiones y estabilidad

Esta es la v1, reflejada en la ruta base /api/public/v1. Es un contrato estable y cuidado, mantenido a propósito aparte de los endpoints internos que usan las aplicaciones.

Los cambios aditivos no rompen nada y los hacemos sin subir la versión: endpoints nuevos, campos opcionales nuevos en la petición, campos nuevos en una respuesta y valores nuevos en un campo enumerado (por ejemplo un valor nuevo de compliance_status). Escribe tu cliente para que los tolere. Ignora los campos de respuesta que no reconozcas en vez de fallar, y trata un valor de enumeración desconocido como una cadena que pasa de largo, no como un error grave.

Los cambios que rompen, que evitamos, serían quitar o renombrar un campo, cambiar el tipo de un campo o cambiar el significado de un endpoint. Si alguna vez tuviéramos que hacer uno, saldría bajo una ruta de versión nueva (/api/public/v2), la versión antigua seguiría funcionando durante un periodo de retirada comunicado con claridad, y lo anunciaríamos en el registro de cambios de abajo antes de quitar nada.

Rotación y guardado de claves

Una clave se muestra entera exactamente una vez, en el momento en que la creas. Guardamos solo un hash con sal (SHA-256), nunca la clave en sí, así que no se puede recuperar ni enviar por correo más tarde. Cópiala a tu almacén de secretos en ese momento. La aplicación puede enseñarte después un prefijo no secreto (ctk_AbC1…) para que distingas unas claves de otras, pero nunca la clave entera otra vez.

Para rotar una clave, crea una nueva, despliégala y revoca la antigua. Revocar es inmediato y permanente: la clave se desactiva (nunca se borra del todo, así que tu historial de auditoría queda intacto) y cada petición posterior con ella devuelve 401 UNAUTHORIZED. Emite claves distintas por integración, y claves de solo lectura para todo lo que solo consulte, así puedes rotar o revocar una sin tocar las demás.

Registro de cambios

v1.1 2026-07-10

  • Webhooks: suscríbete a eventos (calibración registrada o aprobada, instrumento próximo a vencer o vencido, y más) con entrega firmada por HMAC y reintentos.
  • Campos obligatorios: GET /standards/field-requirements publica los campos de calibración que exigen las normas que has seleccionado, para que puedas construir un cuerpo de calibración válido antes de hacer el POST.
  • Lista de vencimientos: GET /due devuelve los instrumentos que vencen o están vencidos dentro de un horizonte, cada uno con su estado de cumplimiento autoritativo, para planificar y para paneles.
  • Marcas de baja: pasa include=retired (instrumentos) o include=voided (calibraciones) en los flujos incrementales para que un registro dado de baja o anulado aparezca en el delta en vez de desaparecer en silencio. Desactivado por defecto; las sincronizaciones existentes no cambian.
  • Adjuntos: pide una URL de subida prefirmada, adjunta un documento a un instrumento (y si quieres a una calibración concreta), lista los documentos de un registro y consigue una URL de descarga de corta duración.

v1 2026-07-09

  • Sincronización incremental: updated_since y sort en instrumentos y calibraciones, más un flujo GET /calibrations para todo el espacio de trabajo.
  • Filtrado de instrumentos: asset_tag, serial_number, compliance_status y next_due_before.
  • Obtención de certificados: GET /calibrations/{id}/certificate devuelve el PDF de la calibración.
  • Baja de instrumentos: POST /instruments/{id}/retire da de baja un instrumento de forma lógica y deja el libro intacto.
  • Cabeceras de límite de peticiones (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset y Retry-After en un 429) en cada respuesta.
  • Un envoltorio de error uniforme ({ code, message, details? }) en todos los endpoints.

v1 Versión inicial

  • Lee y crea instrumentos, registra calibraciones en el libro con detección de manipulaciones y lee las sedes y las normas seleccionadas.
  • Autenticación bearer o X-API-Key, ámbitos de lectura y escritura, acceso desde Professional, límite de 120 por minuto y el envoltorio de listado paginado.

Referencia

La referencia REST completa

Todos los endpoints, parámetros, cuerpos de petición y respuestas, generados a partir de la especificación OpenAPI de la API y presentados como una referencia a pantalla completa con búsqueda.

La referencia se abre en una pestaña nueva, con navegador de búsqueda, esquemas de petición y respuesta y ejemplos para copiar y pegar en cada operación. ¿Prefieres generar un cliente? La especificación OpenAPI de arriba alimenta generadores de código en todos los lenguajes principales.

---