Skip to Content
Ejemplos

API de horario de apertura

Obtén el horario de apertura resuelto y los próximos festivos para un sitio web

Última actualización el

Usa la API de horario de apertura resuelto si muestras en un sitio público horarios de temporada (por ejemplo Zomer / Winter) o los próximos festivos. Tillor ya aplica las excepciones, los periodos de temporada y el horario semanal normal, y añade los festivos legales dentro del rango solicitado. No necesitas volver a implementarlo.

Permiso: organization:settings:read u organization:settings:write

La clave de API se queda en el servidor

Llama a Tillor solo desde un servidor (Route Handler de Next.js, Server Component o backend). Guarda la clave en variables de entorno. No la envíes nunca al navegador.

Autenticación: consulta la API HTTP. Prueba el endpoint en la referencia de la API o mediante OpenAPI (etiqueta organization-settings).

Endpoint

GET https://tillor.eu/api/orgs/org_abc123/organization-settings/opening-hours

Encabezados:

x-api-key: tkn_xxx

Sustituye org_abc123 por el ID de tu organización.

Parámetros de consulta

Usa date o from/to. No los combines.

ParámetroSignificado
(ninguno)Hoy, en la zona horaria de la organización
dateUn solo día natural (YYYY-MM-DD)
fromDía de inicio (incluido). Sin to: solo ese día
toDía final (incluido). Requiere from. Máximo 366 días
languageIsoCodeIdioma de los nombres de los festivos (NL, FR, …). Por defecto: el idioma de la organización

Respuesta

Cada elemento es un día natural, ya resuelto:

CampoSignificado
timeZoneZona horaria IANA de la organización
from / toRango solicitado
holidaysFestivos legales que se solapan con from–to
items[].dateDía natural YYYY-MM-DD
items[].weekdaymonday … sunday
items[].sourceexception, period o regular
items[].periodLabelSolo con source: "period": la etiqueta de Tillor, por ejemplo "Zomer" o "Winter"
items[].holidaySolo si ese día es un festivo legal: { id, name, startDate, endDate }
items[].blocksFranjas horarias en minutos desde la medianoche
items[].closedtrue si blocks está vacío

Prioridad: excepción (festivo o rango personalizado) → último periodo de temporada que coincida → horario semanal normal.

Las etiquetas de temporada provienen de Configuración > Horario de apertura. Los periodos pueden cruzar el cambio de año (por ejemplo del 1 de noviembre al 31 de marzo). Los días fuera de todo periodo recurren al horario semanal normal (source: "regular", sin periodLabel).

De minutos a hora del reloj

startMinutes / endMinutes son minutos desde las 00:00.

MinutosHora
54009:00
60010:00
72012:00
78013:00
102017:00
114019:00
function formatMinutes(minutes: number): string {
  const hour = Math.floor(minutes / 60);
  const minute = minutes % 60;
  return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
}

Ejemplos

Día dentro de un periodo de temporada

curl -X GET "https://tillor.eu/api/orgs/org_abc123/organization-settings/opening-hours?date=2026-07-15" \
  -H "x-api-key: tkn_xxx"
{
  "timeZone": "Europe/Brussels",
  "from": "2026-07-15",
  "to": "2026-07-15",
  "holidays": [],
  "items": [
    {
      "date": "2026-07-15",
      "weekday": "wednesday",
      "source": "period",
      "periodLabel": "Zomer",
      "blocks": [{ "startMinutes": 540, "endMinutes": 720 }],
      "closed": false
    }
  ]
}

09:00–12:00, horario de verano.

Día fuera de los periodos de temporada

curl -X GET "https://tillor.eu/api/orgs/org_abc123/organization-settings/opening-hours?date=2026-01-15" \
  -H "x-api-key: tkn_xxx"
{
  "timeZone": "Europe/Brussels",
  "from": "2026-01-15",
  "to": "2026-01-15",
  "holidays": [],
  "items": [
    {
      "date": "2026-01-15",
      "weekday": "thursday",
      "source": "regular",
      "blocks": [
        { "startMinutes": 540, "endMinutes": 720 },
        { "startMinutes": 780, "endMinutes": 1020 }
      ],
      "closed": false
    }
  ]
}

09:00–12:00 y 13:00–17:00.

Rango que cruza el límite de un periodo

curl -X GET "https://tillor.eu/api/orgs/org_abc123/organization-settings/opening-hours?from=2026-03-30&to=2026-04-02" \
  -H "x-api-key: tkn_xxx"
FechasourceperiodLabel
2026-03-30regular-
2026-03-31regular-
2026-04-01periodZomer
2026-04-02periodZomer

Próximos festivos

Los festivos legales vienen en la misma respuesta: holidays para una lista (nombre + fechas), items[].holiday si ese día natural es festivo. Las horas siguen siendo el horario resuelto; un festivo sin excepción rellenada en Tillor sigue el horario de temporada o el semanal (source no es entonces "exception").

curl -X GET "https://tillor.eu/api/orgs/org_abc123/organization-settings/opening-hours?from=2026-08-27&to=2026-12-31&languageIsoCode=NL" \
  -H "x-api-key: tkn_xxx"
{
  "timeZone": "Europe/Brussels",
  "from": "2026-08-27",
  "to": "2026-12-31",
  "holidays": [
    {
      "id": "0fad0035-6b77-404a-953d-c66dac014483",
      "name": "Allerheiligen",
      "startDate": "2026-11-01",
      "endDate": "2026-11-01"
    },
    {
      "id": "d90e5f53-bc92-4860-bdae-f7e75935848d",
      "name": "Kerstmis",
      "startDate": "2026-12-25",
      "endDate": "2026-12-25"
    }
  ],
  "items": [
    {
      "date": "2026-11-01",
      "weekday": "sunday",
      "source": "regular",
      "holiday": {
        "id": "0fad0035-6b77-404a-953d-c66dac014483",
        "name": "Allerheiligen",
        "startDate": "2026-11-01",
        "endDate": "2026-11-01"
      },
      "blocks": [],
      "closed": true
    }
  ]
}

El ejemplo de items anterior está abreviado; la respuesta real contiene cada día del rango. Las vacaciones escolares no están en este endpoint; siguen siendo GET /holidays?kind=school.

Next.js (servidor)

Para un widget semanal o un “abierto ahora” en un sitio público:

const TILLOR_API_URL = "https://tillor.eu";
const ORGANIZATION_ID = process.env.TILLOR_ORG_ID ?? "";

type OpeningHoursBlock = {
  startMinutes: number;
  endMinutes: number;
};

type OpeningHoursHoliday = {
  id: string;
  name: string;
  startDate: string;
  endDate: string;
};

type OpeningHoursItem = {
  date: string;
  weekday: string;
  source: "exception" | "period" | "regular";
  periodLabel?: string;
  holiday?: OpeningHoursHoliday;
  blocks: OpeningHoursBlock[];
  closed: boolean;
};

type OpeningHoursResponse = {
  timeZone: string;
  from: string;
  to: string;
  holidays: OpeningHoursHoliday[];
  items: OpeningHoursItem[];
};

export async function getOpeningHours(
  from: string,
  to: string,
): Promise<OpeningHoursResponse> {
  const url = new URL(
    `${TILLOR_API_URL}/api/orgs/${ORGANIZATION_ID}/organization-settings/opening-hours`,
  );
  url.searchParams.set("from", from);
  url.searchParams.set("to", to);
  url.searchParams.set("languageIsoCode", "NL");

  const response = await fetch(url, {
    headers: {
      "x-api-key": process.env.TILLOR_API_KEY ?? "",
    },
    next: { tags: ["opening-hours"], revalidate: 3600 },
  });

  if (!response.ok) {
    throw new Error(`Tillor opening hours ${response.status}`);
  }

  return response.json() as Promise<OpeningHoursResponse>;
}

holidays es la lista para un bloque de “próximos festivos”. items[].holiday es útil en un calendario semanal. items[].periodLabel es el texto de temporada (por ejemplo "Zomer" o "Winter"). Una caché de 1 hora suele bastar.

Invalidar la caché (webhook)

En cada cambio de los ajustes de la organización, Tillor envía organizationSettings:updated (webhook y SSE). Suscríbete a él y vuelve a obtener el horario de apertura. El payload es pequeño a propósito (sin IP de impresoras ni el horario completo):

{
  "event": "organizationSettings:updated",
  "data": {
    "openingHoursChanged": true
  },
  "timestamp": 1787829600000
}

openingHoursChanged es true si cambia el horario de apertura, la zona horaria o el país por defecto, que es lo que afecta a GET .../opening-hours. Con false puedes dejar la caché del horario de apertura como está.

Ejemplo de Route Handler de Next.js:

import { revalidateTag } from "next/cache";

export async function POST(request: Request) {
  const body = (await request.json()) as {
    event?: string;
    data?: { openingHoursChanged?: boolean };
  };
  if (body.event === "organizationSettings:updated" && body.data?.openingHoursChanged) {
    await revalidateTag("opening-hours");
  }
  return new Response(null, { status: 204 });
}

Crear un webhook: consulta Webhooks. Clave de evento: organizationSettings:updated (permiso organization:settings:read u organization:settings:write).

Horario completo (opcional)

Solo es necesario si quieres mostrar tú mismo el calendario de temporadas (etiquetas + fromMonth/untilDay), sin resolverlo por día:

GET https://tillor.eu/api/orgs/org_abc123/organization-settings

Lee openingHoursSchedule.periods (etiquetas como "Zomer") y openingHoursSchedule.weekdays (horario normal). Para el propio sitio web, GET .../opening-hours sigue siendo la fuente más sencilla.

MCP

En Cursor o Claude: endpoint organizationSettings_listOpeningHours (categoría organizationSettings), argumentos organizationId, opcionalmente date o from/to, opcionalmente languageIsoCode.

Relacionado

¿Te ha resultado útil esta página?

En esta página