Saltar al contenido
El Registro de Jornada .com

Portal del desarrollador · en preparación

Empezar con la API de lectura

Claves, ámbitos, entornos y una primera llamada en cinco lenguajes. Todo lo que hay aquí describe lo que el servidor hace hoy.

Paso a paso

De la clave a la primera llamada

  1. 01

    Crea una clave

    En la aplicación, Más → Integraciones, solo la administración. Las claves no se crean por API. Se muestra completa una sola vez; de ella solo guardamos su huella.

  2. 02

    Dale los ámbitos justos

    Cada clave nace sin permisos. Pide los mínimos, y si quieres, una caducidad y una lista de IP permitidas.

  3. 03

    Empieza en pruebas

    Una clave erj_test_ lee una empresa ficticia. Una erj_live_ lee tus datos reales.

  4. 04

    Llama con la cabecera

    Authorization: Bearer erj_… a la dirección base, sin parámetro de empresa: sale de la clave.

Dirección base: https://app.elregistrodejornada.com/api/v1. Es el servidor de la aplicación; un dominio propio para la API (api.elregistrodejornada.com) está pendiente.

Entornos

Pruebas y datos reales, sin mezclarse

Diferencias entre la clave de pruebas y la de producción
Característicaerj_test_ (pruebas)erj_live_ (real)
Qué leeSolo la empresa ficticia emp_sandbox: 8 personas, los últimos 10 días laborables con fichajes, ausencias y turnos.Los datos de tu empresa, y nunca los del entorno de pruebas.
DatosSe generan siempre igual y los fichajes llevan una cadena de integridad válida. Cierres, informes, auditoría y eventos salen vacíos.Los reales, con los mismos ámbitos y paginación.
Cupo mensualNo lo gasta. Sí tiene límite por minuto.Cuenta contra el cupo del plan.
DirecciónLa misma dirección base; solo cambia el tipo de clave. Un dominio propio para pruebas está pendiente.La dirección base.

El entorno de pruebas tampoco escribe: una clave erj_test_ da 403 escritura_no_disponible_en_pruebas en las rutas de escritura.

Ámbitos

Cada clave lee solo lo que le das

Sin el ámbito, la API responde 403 con el código ambito_insuficiente. Esta lista sale del contrato.

Ámbitos de lectura de la API
ÁmbitoQué permite
personas:leerLeer personas
personas:nifVer el NIF completo
fichajes:leerLeer fichajes (sin posición ni nota)
modificaciones:leerLeer los expedientes de modificación de fichajes (sin motivo ni notas)
ausencias:leerLeer ausencias
turnos:leerLeer turnos
bolsa:leerLeer los movimientos de la bolsa de horas
cierres:leerLeer cierres
incidencias:leerLeer las incidencias de jornada
informes:leerListar informes y exportaciones (solo metadatos)
integridad:leerLeer el estado de integridad de la cadena
auditoria:leerLeer la auditoría
eventos:leerLeer el flujo de eventos
empresa:leerLeer los centros y los días del calendario de la empresa
calendario:leerLeer el calendario de equipo (solo «Ausente»)
  • personas:nif: Muestra el NIF completo. Sin él, sale enmascarado (***5678Z).
  • fichajes:posicion: Añade posición y nota a los fichajes. Es un ámbito delicado: sin él no salen (minimización de datos). No se puede conceder por OAuth.
  • auditoria:leer: Ámbito delicado: concédelo solo a quien administra.
  • Los ámbitos de escritura (fichajes:escribir, ausencias:escribir, modificaciones:escribir) no se conceden por defecto ni por OAuth y solo valen si la empresa activa además la opción correspondiente.

Los fichajes salen sin posición ni nota salvo con fichajes:posicion, por minimización de datos (art. 5.1.c del Reglamento (UE) 2016/679). BOEReglamento general de protección de datos, art. 5

Ejemplos

La misma llamada en cinco formas

Cada ejemplo lista las cinco primeras personas y muestra lo que te queda de la ventana por minuto. Cámbiales la clave por la tuya.

curl

export ERJ_CLAVE="erj_test_..."   # tu clave de pruebas
curl -H "Authorization: Bearer $ERJ_CLAVE" \
  "https://app.elregistrodejornada.com/api/v1/personas?limit=5"

El resto de rutas, con sus filtros, están en la referencia.

TypeScript (Node.js 22.6 o superior)

Probado: se ejecuta contra un servidor local que imita el contrato y el compilador de tipos no da errores.

// Inicio rápido (Node 22.6 o superior):  ERJ_CLAVE=erj_test_... node ejemplos/inicio-rapido.ts
// Lista las primeras personas y muestra lo que te queda de la ventana por minuto.
import { ClienteErj, ErrorApi } from '../src/index.ts';

const cliente = new ClienteErj({ clave: process.env.ERJ_CLAVE!, urlBase: process.env.ERJ_URL_BASE });
try {
  const pagina = await cliente.personas.listar({ limit: 5 });
  for (const persona of pagina.datos) console.log(persona.id, persona.nombre);
  console.log('Te quedan', cliente.limites?.restantes, 'llamadas este minuto.');
} catch (e) {
  if (e instanceof ErrorApi) console.error(`Error ${e.estado} (${e.codigo}), petición ${e.idPeticion}`);
  else throw e;
}

Python (3.9 o superior)

Probado: se ejecuta contra un servidor local que imita el contrato.

"""Inicio rápido:  ERJ_CLAVE=erj_test_... python ejemplos/inicio_rapido.py
Lista las primeras personas y muestra lo que te queda de la ventana por minuto."""
import os
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from erj import Cliente, ErrorApi  # noqa: E402

opciones = {"url_base": os.environ["ERJ_URL_BASE"]} if os.environ.get("ERJ_URL_BASE") else {}
cliente = Cliente(os.environ["ERJ_CLAVE"], **opciones)
try:
    pagina = cliente.personas.listar(limit=5)
    for persona in pagina["datos"]:
        print(persona["id"], persona["nombre"])
    print("Te quedan", cliente.limites.restantes, "llamadas este minuto.")
except ErrorApi as e:
    print(f"Error {e.estado} ({e.codigo}), petición {e.id_peticion}", file=sys.stderr)
    sys.exit(1)

C# (.NET 6 o superior)

Probado: compila y se ejecuta contra un servidor local que imita el contrato.

// Inicio rápido:  ERJ_CLAVE=erj_test_... dotnet run   (desde sdk/csharp/inicio-rapido)
// Lista las primeras personas y muestra lo que te queda de la ventana por minuto.
using ElRegistroJornada;

var cliente = new ClienteErj(new OpcionesCliente
{
    Clave = Environment.GetEnvironmentVariable("ERJ_CLAVE") ?? "",
    UrlBase = Environment.GetEnvironmentVariable("ERJ_URL_BASE") ?? ClienteErj.UrlBaseProduccion,
});
try
{
    var pagina = await cliente.Personas.ListarAsync(new Dictionary<string, string?> { ["limit"] = "5" });
    foreach (var persona in pagina.Datos)
        Console.WriteLine($"{persona!["id"]} {persona["nombre"]}");
    Console.WriteLine($"Te quedan {cliente.UltimosLimites?.Restantes} llamadas este minuto.");
    return 0;
}
catch (ErrorApi e)
{
    Console.Error.WriteLine($"Error {e.Estado} ({e.Codigo}), petición {e.IdPeticion}");
    return 1;
}

PHP (8.1 o superior)

Sin ejecutar: la librería y este ejemplo están escritos pero no se han podido probar porque en el equipo de desarrollo no hay intérprete de PHP. Ejecútalo con php antes de fiarte de él.

<?php
/**
 * Inicio rápido:  ERJ_CLAVE=erj_test_... php ejemplos/inicio-rapido.php
 * ESTADO: NO EJECUTADO (no hay intérprete de PHP en el equipo donde se escribió).
 */
declare(strict_types=1);

require __DIR__ . '/../src/ClienteErj.php';

use ElRegistroJornada\ClienteErj;
use ElRegistroJornada\ErrorApi;

$cliente = new ClienteErj(getenv('ERJ_CLAVE') ?: '', getenv('ERJ_URL_BASE') ?: ClienteErj::URL_BASE_PRODUCCION);
try {
    $pagina = $cliente->personas->listar(['limit' => 5]);
    foreach ($pagina['datos'] as $persona) {
        echo $persona['id'], ' ', $persona['nombre'] ?? '', "\n";
    }
    echo 'Te quedan ', $cliente->limites?->restantes, " llamadas este minuto.\n";
} catch (ErrorApi $e) {
    fwrite(STDERR, "Error {$e->estado} ({$e->codigo}), petición {$e->idPeticion}\n");
    exit(1);
}

Paginación

Las listas se recorren con cursor

Las listas devuelven datos y siguiente, un cursor opaco que valenull en la última página. Pásalo tal cual en cursor con los mismos filtros; con otros, 400 cursor_invalido. limit va de 1 a 200 (50 por defecto).

eventos es distinto: el cursor se pasa como desde y la respuesta trae ademáshayMas. Los fichajes admiten como mucho 31 días por llamada (422 rango_demasiado_amplio): pide varios tramos.

GET /personas?limit=50
GET /personas?limit=50&cursor=eyJ...

OAuth

Gestorías e integradores con varias empresas

Para acceder a varias empresas hay un flujo de código de autorización con PKCE S256obligatorio, de solo lectura: la administración de cada empresa concede el acceso con ámbitos concretos. El token de acceso (erj_oat_…) dura una hora y se acepta en losGET igual que una clave; el de refresco es rotatorio y reutilizarlo revoca la autorización. Los puntos de entrada están en la referencia.

OAuth es de lectura: sus dos puntos POST solo canjean y revocan tokens.

Webhooks

Los avisos llevan firma

Las suscripciones se crean, rotan y desactivan desde la aplicación (Más → Integraciones): la API pública no tiene rutas para gestionarlas. Cada entrega lleva ERJ-Timestamp y ERJ-Signature:v1= más el HMAC-SHA256, en hexadecimal, de <timestamp>.<cuerpo> con el secreto del webhook. Verifica sobre el cuerpo exacto recibido, rechaza lo que tenga más de 300 segundos y deduplica porERJ-Event-Id: la entrega es «al menos una vez».

Las cuatro librerías incluyen la verificación, probadas contra 30 vectores compartidos (la de PHP, sin ejecutar). Si no responden con 2xx, se reintenta dentro de las 24 h. Los avisos se envían en una pasada diaria del servidor (04:00 UTC), no al instante: no los uses para nada que deba ser inmediato.

Verificar sin fiarse de nosotros

Comprueba el registro por tu cuenta

La lista de GET /fichajes no es la entrada del verificador independiente: es una proyección de lectura sin todos los campos del resumen. Para recalcular la cadena sin conexión y sin cuenta, usa el paquete pericial y la página de verificación.GET /integridad recalcula la cadena en el servidor, pero eso sí exige fiarse del servidor.

Qué falta

Lo que todavía no existe

  • Servicio abierto: la API no se puede contratar y no hay fecha.
  • Escritura abierta: las tres escrituras (fichar, solicitar una ausencia, proponer una corrección) existen en el servidor pero apagadas, sin fecha. No hay alta de personas por API, y los asientos de fichaje no se modifican ni se borran en ningún caso.
  • Librerías publicadas en los gestores de paquetes, y la de PHP, sin ejecutar.
  • Dominio propio de la API y del entorno de pruebas.
  • Referencia interactiva para probar llamadas desde el navegador: hoy la referencia es de lectura.
  • Los límites concretos por plan están por confirmar: ver límites y errores.