Aller au contenu
API pública · gratuita

Documentação da API YA-Planning

Integre a geração e a consulta de horários, faça a gestão da configuração dos seus estabelecimentos e receba eventos em tempo real. Referência completa, exemplos e ambiente de testes.

Introdução

A API é REST, em JSON, versionada. Todos os recursos de negócio vivem sob o prefixo /api/v1/pub/v1. Os endpoints de demonstração públicos estão em /api/v1/public/api-demo.

  • Base URL: https://o-seu-dominio/api/v1/pub/v1
  • Formato: pedidos e respostas em application/json (exceto exportações ICS/PDF).
  • Versionamento: a versão está no caminho (/v1); as adições retrocompatíveis não alteram a versão.
  • CORS: a API pública está aberta a aplicações de navegador de terceiros (sem cookies).

Autenticação

Cada pedido leva uma chave API, num de dois cabeçalhos:

X-API-Key: yap_live_xxxxxxxxxxxxxxxxxxxx
# ou
Authorization: Bearer yap_live_xxxxxxxxxxxxxxxxxxxx

Uma chave pode estar autorizada para vários estabelecimentos (chave empresa). Indique então o destino através do cabeçalho X-Etablissement-Id; se só houver um estabelecimento autorizado, é deduzido automaticamente. As chaves são criadas, com âmbitos, na consola.

Ambientes e ambiente de testes

Dois ambientes, distinguidos pelo prefixo da chave:

  • yap_live_… — produção: os seus dados reais; a geração desconta da quota semanal.
  • yap_test_… — sandbox: as leituras devolvem um conjunto de dados de demonstração (nunca os seus dados reais), a geração não é descontada, e a escrita de configuração é recusada (SANDBOX_READONLY). Ideal para desenvolver sem risco.

Âmbitos

Cada chave leva um subconjunto de âmbitos; um endpoint recusa (403 INSUFFICIENT_SCOPE) se o âmbito requerido faltar.

config:readLer ciclos, turmas, disciplinas, professores, blocos, volumes horários.
config:writeCriar / modificar / eliminar a configuração do estabelecimento.
timetables:readListar os trabalhos, ler os resultados e os horários publicados, exportar.
timetables:writeLançar uma geração e publicar um horário.
webhooks:manageCriar, listar e eliminar endpoints webhook.
establishments:manageCriar e listar os seus próprios estabelecimentos (nível conta, sem X-Etablissement-Id).

Débito e quotas

Cada chave tem um limite de débito (60 req/min por defeito). As respostas levam:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 1783919311   # epoch (s) de réinitialisation

Ultrapassá-lo devolve 429 RATE_LIMITED com o cabeçalho Retry-After. A geração de horários está além disso sujeita à quota semanal do plano (visível através de /me).

Toda a resposta da API leva também um cabeçalho X-Request-Id (identificador de correlação) — comunique-o ao suporte em caso de incidente. Nota: a contagem de débito é atualmente aplicada por instância; num destacamento multi-instância o limite efetivo pode ser um múltiplo do valor apresentado.

Erros

Envelope de erro uniforme, com um requestId para o suporte:

{ "error": "INSUFFICIENT_SCOPE", "message": "Scope requis : config:write.",
  "requestId": "6181762d-…", "requiredScope": "config:write" }
CódigoerrorCausa
400BAD_REQUESTParâmetros ausentes ou inválidos.
401UNAUTHORIZED / INVALID_API_KEYChave ausente, inválida ou revogada.
403INSUFFICIENT_SCOPEA chave não possui o âmbito requerido.
403ETABLISSEMENT_NOT_GRANTEDA chave não está autorizada para o estabelecimento pedido.
403SANDBOX_READONLYEscrita proibida com uma chave sandbox (só leitura).
402PLAN_UPGRADE_REQUIREDFunção reservada a um plano pago (ex. exportação iCal).
404NOT_FOUNDRecurso inexistente ou fora do seu estabelecimento.
409WORKFLOW_ERRORTransição de workflow impossível (ex. publicar um rascunho).
429RATE_LIMITEDDébito ultrapassado — tente de novo após o cabeçalho Retry-After.

Testar em direto — sem registo

Endpoints de demonstração públicos (nenhuma chave necessária).

Descarregar OpenAPI

Postman: «Import» → «Link» → cole /q/openapi?format=json.

Referência — Conta e introspeção

GET/me

Detalhes da chave atual: ambiente, estabelecimento destino, âmbitos concedidos e quota de geração restante. Não requer âmbito.

Resposta
{
  "environment": "LIVE",
  "etablissementId": "61a9…8cb",
  "scopes": ["config:read", "timetables:write"],
  "entitlements": {
    "plan": "PRO",
    "generationsRestantes": -1,
    "resetLe": "2026-07-14T00:00:00Z",
    "maxClassesExport": -1
  }
}
GET/me/etablissement

Perfil público do estabelecimento destino: nome, sigla, cidade, país, tipo, sítio web. Não requer âmbito.

Resposta
{"id":"…","nom":"Groupe Scolaire Les Palmiers","slug":"gs-palmiers","sigle":"GSP","ville":"Abidjan","pays":"CI","type":"PRIVE","siteWeb":"https://…"}
GET/me/usage

Utilização recente da chave atual (chamadas e erros por dia). Não requer âmbito.

Parâmetros:
  • days — janela em dias (por defeito 30, máx 90)
Resposta
{"apiKeyId":"…","days":30,"daily":[{"day":"2026-07-12","calls":128,"errors":3}]}

Referência — Conta — estabelecimentos

Aprovisionamento ao nível da conta: crie os seus próprios estabelecimentos SEM o cabeçalho X-Etablissement-Id (o caminho /account não está ligado a um estabelecimento). Âmbito establishments:manage. Na criação, a chave chamadora recebe automaticamente uma concessão ACTIVE sobre o novo estabelecimento — depois use X-Etablissement-Id para o configurar e gerar.

POST/account/etablissementsestablishments:manage

Criar um estabelecimento (bucket de programador, sujeito ao limite da sua conta).

Corpo
{"nom":"Groupe Scolaire Les Palmiers","type":"PRIVE_LAIC","pays":"CI"}
Resposta
{"id":"…","nom":"Groupe Scolaire Les Palmiers","slug":"gs-les-palmiers","pays":"CI","type":"PRIVE_LAIC"}
GET/account/etablissementsestablishments:manage

Listar os estabelecimentos que criou.

Resposta
[{"id":"…","nom":"…","slug":"…","pays":"CI","type":"PRIVE_LAIC"}]

Referência — Configuração

Estrutura pedagógica do estabelecimento. Leitura: âmbito config:read. Escrita: âmbito config:write (proibida em sandbox). Um estabelecimento recém-criado já contém um «Ciclo principal».

GET/cyclesconfig:read

Lista de ciclos.

Resposta
[{"id":"…","libelle":"Cycle principal","ordre":0}]
POST/cyclesconfig:write

Criar um ciclo.

Corpo
{"libelle":"Second cycle","ordre":1}
DELETE/cycles/{id}config:write

Eliminar um ciclo.

GET/niveauxconfig:read

Lista de turmas/níveis.

Resposta
[{"id":"…","libelle":"6ème 1","cycleId":"…","effectif":42,"ordre":1}]
POST/niveauxconfig:write

Criar uma turma.

Corpo
{"libelle":"6ème 3","cycleId":"<uuid-cycle>","effectif":40,"ordre":3}
PUT/niveaux/{id}config:write

Modificar uma turma (campos parciais).

DELETE/niveaux/{id}config:write

Eliminar uma turma.

GET/disciplinesconfig:read

Lista de disciplinas.

Resposta
[{"id":"…","libelle":"Mathématiques","code":"MATH","couleur":"#6C5CE7","ordre":1}]
POST/disciplinesconfig:write

Criar uma disciplina.

Corpo
{"libelle":"Physique-Chimie","code":"PC","couleur":"#00B894","ordre":5}
PUT/disciplines/{id}config:write

Modificar uma disciplina.

DELETE/disciplines/{id}config:write

Eliminar uma disciplina.

GET/professeursconfig:read

Lista de professores.

Resposta
[{"id":"…","nom":"Koné","prenoms":"Awa","matricule":"P-0142","email":"…","telephone":"…","nombreHeureAutorise":18,"statut":"TITULAIRE","actif":true}]
POST/professeursconfig:write

Criar um professor.

Corpo
{"nom":"Diarra","prenoms":"Sékou","matricule":"P-0210","email":"s.diarra@ecole.ci","nombreHeureAutorise":18,"statut":"VACATAIRE","actif":true}
PUT/professeurs/{id}config:write

Modificar um professor.

DELETE/professeurs/{id}config:write

Eliminar um professor.

GET/horairesconfig:read

Blocos horários da grelha.

Resposta
[{"id":"…","jour":"MONDAY","heureDebut":"08:00","heureFin":"09:00","estPause":false,"libelle":null}]
POST/horairesconfig:write

Criar um bloco (dia, HH:mm).

Corpo
{"jour":"MONDAY","heureDebut":"08:00","heureFin":"09:00","estPause":false,"libelle":"M1"}
DELETE/horaires/{id}config:write

Eliminar um bloco.

GET/volumesconfig:read

Volumes horários (disciplina × ciclo).

Resposta
[{"id":"…","cycleId":"…","disciplineId":"…","nombreHeuresObligatoire":4,"minHeuresConsecutives":1,"maxHeuresConsecutives":2}]
POST/volumesconfig:write

Criar um volume horário.

Corpo
{"cycleId":"<uuid>","disciplineId":"<uuid>","nombreHeuresObligatoire":4,"minHeuresConsecutives":1,"maxHeuresConsecutives":2}
DELETE/volumes/{id}config:write

Eliminar um volume horário.

Referência — Horários

Geração assíncrona (o solver corre em segundo plano — consulte o estado), publicação e consulta. Em sandbox, a geração usa um conjunto de demonstração e não é descontada da quota.

POST/timetables/generatetimetables:write

Lança uma geração. Devolve 202 + um jobId a consultar. LIVE desconta da quota semanal. Cabeçalho opcional Idempotency-Key: um mesmo identificador repete a resposta memorizada (resposta marcada Idempotency-Replayed: true) em vez de relançar uma geração.

Parâmetros:
  • Idempotency-Key — cabeçalho (opcional) — chave de idempotência no lado LIVE
Corpo
{"durationMinutes": 5}
Resposta
{"jobId":"…","status":"SOLVING_SCHEDULED"}
GET/timetables/jobstimetables:read

Lista paginada dos trabalhos.

Parâmetros:
  • page — índice de página (por defeito 0)
  • size — tamanho (por defeito 25, máx 100)
Resposta
{"items":[{"jobId":"…","name":"…","status":"SOLVING_ENDED","workflowStatus":"BROUILLON","feasible":true,"hardScore":0,"softScore":-42,"createdAt":"…"}],"total":7,"page":0,"size":25}
GET/timetables/jobs/{jobId}timetables:read

Estado de um trabalho.

GET/timetables/jobs/{jobId}/resulttimetables:read

Resultado: lista das lições (uma vez status = SOLVING_ENDED).

Resposta
{"jobId":"…","status":"SOLVING_ENDED","feasible":true,"lessons":[{"id":"1","matiere":"Maths","niveau":"6ème 1","professeur":"M. Koné","jour":"MONDAY","debut":"08:00:00","fin":"09:00:00","couleur":"#6C5CE7","niveauOrdre":1,"verrouille":false}]}
POST/timetables/jobs/{jobId}/publishtimetables:write

Publica o horário (workflow → PUBLIE). Dispara o evento webhook timetable.published.

GET/timetables/published/latesttimetables:read

Último horário publicado do estabelecimento.

GET/timetables/published/{jobId}/classestimetables:read

Rótulos das turmas presentes num horário publicado.

Resposta
{"classes":["6ème 1","6ème 2","5ème 1"]}
GET/timetables/published/{jobId}/classe/{niveau}timetables:read

Lições de uma turma de um horário publicado.

GET/timetables/jobs/{jobId}/export.icstimetables:read

Exportação iCalendar (text/calendar). Reservada ao plano Pro → 402 caso contrário.

Parâmetros:
  • niveau — filtrar por uma turma
  • professeur — filtrar por um professor
GET/timetables/jobs/{jobId}/export.pdftimetables:read

Exportação PDF (application/pdf) — toda a promoção, por turma ou por professor.

Parâmetros:
  • mode — all | class | teacher
  • niveau — obrigatório se mode=class
  • professeur — obrigatório se mode=teacher

Referência — Webhooks

Receba eventos assinados. Âmbito webhooks:manage. O segredo só é devolvido na criação (ou após rotação). Eventos: timetable.job.completed, timetable.job.failed, timetable.published, webhook.test. Cada entrega é assinada (HMAC-SHA256) e repetida até 5 vezes em caso de falha. O URL é validado contra o SSRF (destinos internos/privados recusados).

GET/webhookswebhooks:manage

Lista de endpoints.

POST/webhookswebhooks:manage

Criar um endpoint. Devolve o segredo de assinatura (guarde-o).

Corpo
{"url":"https://exemple.com/webhooks/yap","events":["timetable.published","timetable.job.completed"]}
Resposta
{"endpoint":{"id":"…","url":"…","events":["timetable.published"],"active":true},"secret":"whsec_…"}
DELETE/webhooks/{id}webhooks:manage

Eliminar um endpoint.

GET/webhooks/{id}/deliverieswebhooks:manage

Registo das entregas (estado, código HTTP, tentativas).

POST/webhooks/{id}/rotatewebhooks:manage

Regenerar o segredo de assinatura (devolvido uma só vez).

Resposta
{"endpoint":{"id":"…","url":"…"},"secret":"whsec_…"}
POST/webhooks/{id}/testwebhooks:manage

Enviar um evento webhook.test para o endpoint (202 + status queued).

POST/webhooks/deliveries/{deliveryId}/replaywebhooks:manage

Repetir uma entrega passada (202 + status queued).

Referência — Demo (pública, sem chave)

Endpoints públicos não autenticados para descobrir a forma das respostas.

GET/api/v1/public/api-demo/preview

Pré-visualização de um estabelecimento de demonstração (ciclos, turmas, disciplinas, professores, blocos).

GET/api/v1/public/api-demo/sample-timetable

Exemplo de lições (forma de um horário gerado).

Webhooks — verificar uma assinatura

Cada entrega é um POST JSON com os cabeçalhos X-Webhook-Event, X-Webhook-Timestamp e X-Webhook-Signature = HMAC-SHA256(timestamp + "." + body, secret). Verifique a assinatura e a frescura do timestamp (menos de 5 min) para evitar a repetição. Eventos: timetable.published, timetable.job.completed, timetable.job.failed, webhook.test.

POST https://votre-app/webhook
X-Webhook-Event: timetable.published
X-Webhook-Timestamp: 1712345678
X-Webhook-Signature: 9f86d081…

{ "event":"timetable.published", "jobId":"…", "etablissementId":"…", "occurredAt":"…" }
import crypto from "crypto";

// Express — vérifier la signature d'un webhook YA-Planning
app.post("/webhooks/yap", express.raw({ type: "*/*" }), (req, res) => {
  const ts  = req.header("X-Webhook-Timestamp");
  const sig = req.header("X-Webhook-Signature");
  const body = req.body.toString("utf8");

  const expected = crypto.createHmac("sha256", process.env.YAP_WEBHOOK_SECRET)
    .update(`${ts}.${body}`).digest("hex");

  // fraîcheur (anti-rejeu) : < 5 min
  const fresh = Math.abs(Date.now()/1000 - Number(ts)) < 300;
  if (!fresh || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)))
    return res.sendStatus(400);

  const event = JSON.parse(body); // { event, jobId, etablissementId, occurredAt }
  res.sendStatus(200);
});

Exemplos de código

# Vérifier ses droits
curl https://votre-domaine/api/v1/pub/v1/me \
  -H "X-API-Key: yap_live_xxxxxxxxxxxx"

# Générer un EDT puis suivre le job
JOB=$(curl -s -X POST .../api/v1/pub/v1/timetables/generate \
  -H "X-API-Key: yap_live_xxxx" -H "Content-Type: application/json" \
  -d '{"durationMinutes":5}' | jq -r .jobId)

curl .../api/v1/pub/v1/timetables/jobs/$JOB \
  -H "X-API-Key: yap_live_xxxx"

Versões e descontinuação

A API está versionada no caminho (/api/v1/pub/v1/…). Adicionamos campos e endpoints de forma retrocompatível; estas adições não constituem uma rutura, por isso programe de forma tolerante (ignore os campos desconhecidos). Toda a rutura dará lugar a uma nova versão maior e a um pré-aviso de 90 dias mínimo, anunciado através do cabeçalho de resposta Deprecation e do registo de alterações abaixo.

Registo de alterações
  • 2026-07 — Contas de programador e painel; aprovisionamento ao nível da conta (POST /account/etablissements, âmbito establishments:manage); endpoints /cycles; exportação PDF mode=teacher.
  • 2026-07 — Idempotency-Key na geração; endpoints /me/etablissement, /me/usage; rotação / teste / repetição de webhooks; evento timetable.job.failed; cabeçalho X-Request-Id; paginação das listas de configuração; expiração e restrição de IP das chaves; validação anti-SSRF dos URL de webhook.
  • 2026-06 — Lançamento da API pública v1: chaves com âmbitos, configuração, geração e publicação de horários, webhooks assinados, sandbox, cabeçalhos de quota.

Pronto para integrar?

Crie uma chave sandbox em 30 segundos e faça a sua primeira chamada.

Abrir a consola API