Sincronização da Estrutura Académica
Esta página explica como sincronizar cada entidade académica do seu sistema para o DokStamp, na ordem correta de dependências.
Siga sempre a ordem indicada abaixo. Criar um certificado antes de existirem as suas dependências resultará em erros de validação 422.
Ordem de sincronização
| Passo | Evento no seu sistema | Ação no DokStamp |
|---|
| 1 | Instituição configurada | POST /institutions (ou configuração manual) |
| 2 | Curso criado / atualizado | POST /courses ou PATCH /courses/{uuid} |
| 3 | Módulo/disciplina criado / atualizado | POST /modules ou PATCH /modules/{uuid} |
| 4 | Módulo adicionado ao curso | POST /courses/{uuid}/attach/modules |
| 5 | Módulos organizados em grupos | POST /courses/{uuid}/modules/groups |
| 6 | Turma (classe de entrada) criada | POST /cohorts |
| 7 | Estudante torna-se elegível | pesquisa → POST /students (se novo) |
| 8 | Estudante matriculado no curso | POST /enrollments |
| 9 | Emissão de certificado ativada | POST /files + POST /certificates |
Passo 2 — Sincronizar um curso
Quando um curso é criado ou atualizado no seu sistema, replique a alteração no DokStamp.
async function syncCourse(course) {
// Search by code to check if it already exists
const existing = await api.get('/courses', {
params: { 'where[code]': course.code }
});
if (existing.data.length > 0) {
// Update existing
await api.patch(`/courses/${existing.data[0].uuid}`, {
name: course.name,
description: course.description,
workload_hours: course.workloadHours,
status: course.isActive ? 'active' : 'archived',
});
return existing.data[0].uuid;
}
// Create new
const res = await api.post('/courses', {
name: course.name,
code: course.code,
institution_uuid: INSTITUTION_UUID,
workload_hours: course.workloadHours,
status: 'active',
});
return res.data.uuid;
}
Passo 3 — Sincronizar módulos/disciplinas
async function syncModule(module) {
const existing = await api.get('/modules', {
params: {
'where[code]': module.code,
'where[institution_uuid]': INSTITUTION_UUID,
}
});
if (existing.data.length > 0) {
await api.patch(`/modules/${existing.data[0].uuid}`, {
name: module.name,
workload: module.workload,
credits: module.credits,
});
return existing.data[0].uuid;
}
const res = await api.post('/modules', {
name: module.name,
code: module.code,
institution_uuid: INSTITUTION_UUID,
workload: module.workload,
credits: module.credits,
level: module.level, // 'undergraduate' | 'graduate' | 'technical' | 'open'
modality: module.modality, // 'in_person' | 'online' | 'hybrid'
});
return res.data.uuid;
}
Passo 4 — Associar módulos a um curso
Após criar/sincronizar o curso e os seus módulos, associe-os:
await api.post(`/courses/${courseUuid}/attach/modules`, {
modules: moduleUuids.map((uuid, index) => ({
uuid,
order: index + 1,
is_required: true,
}))
});
A associação é idempotente para novos módulos, mas re-associar um módulo já associado devolverá um erro 422. Consulte primeiro GET /courses/{uuid}/attach/modules para obter a lista de módulos ainda não associados.
Passo 5 — Grupos de módulos (opcional)
Se o seu sistema organiza disciplinas em semestres ou períodos, replique essa estrutura:
async function syncModuleGroup(courseUuid, group) {
const res = await api.post(`/courses/${courseUuid}/modules/groups`, {
name: group.name, // e.g. "1st Semester", "Core Modules"
order: group.order,
});
return res.data.uuid;
}
De seguida, re-associe os módulos especificando o grupo:
await api.post(`/courses/${courseUuid}/attach/modules`, {
modules: [{
uuid: moduleUuid,
order: 1,
course_module_group_uuid: groupUuid,
}]
});
Passo 6 — Sincronizar turmas
Uma turma corresponde a uma classe de conclusão específica (ex.: “Turma Noturna 2024/1”):
async function syncCohort(cohort) {
const existing = await api.get('/cohorts', {
params: { 'where[code]': cohort.code }
});
if (existing.data.length > 0) return existing.data[0].uuid;
const res = await api.post('/cohorts', {
course_uuid: cohort.courseUuid,
code: cohort.code,
modality: cohort.modality,
start_date: cohort.startDate,
end_date: cohort.endDate,
});
return res.data.uuid;
}
Passo 7 — Registar estudantes elegíveis
Quando o sistema determina que um estudante é elegível para um certificado, registe-o no DokStamp (ou verifique se já existe):
async function syncStudent(student) {
// Always search by email first
const existing = await api.get('/students', {
params: { 'where[email]': student.email }
});
if (existing.data.length > 0) return existing.data[0].uuid;
const res = await api.post('/students', {
name: student.name,
email: student.email,
date_of_birth: student.dateOfBirth, // YYYY-MM-DD
gender: student.gender,
});
return res.data.uuid;
}
Passo 8 — Criar a matrícula
const enrollment = await api.post('/enrollments', {
student_uuid: studentUuid,
course_uuid: courseUuid,
cohort_uuid: cohortUuid, // optional
enrolled_at: student.enrolledAt,
completion_status: 'completed',
grade: student.finalGrade,
completed_at: student.completedAt,
});
Passo 9 — Emitir o certificado
// 1. Upload the PDF
const fileRes = await api.post('/files', formDataWithPdf);
const fileUuid = fileRes.data[0].uuid;
// 2. Issue the certificate
const cert = await api.post('/certificates', {
institution_uuid: INSTITUTION_UUID,
course_uuid: courseUuid,
student_uuid: studentUuid,
cohort_uuid: cohortUuid,
enrollment_uuid: enrollmentUuid,
file_uuid: fileUuid,
status: 'issued',
issued_at: new Date().toISOString(),
});
console.log('Verification URL:', cert.data.public_verification_url);
Exemplo assíncrono completo
Para integrações baseadas em fila, encapsule cada passo num trabalho:
// jobs/SyncCourseJob.js
export async function handle({ course }) {
try {
const uuid = await syncCourse(course);
await syncModules(uuid, course.modules);
logger.info(`Course synced: ${uuid}`);
} catch (err) {
if (err.response?.status >= 500 || err.response?.status === 429) {
throw err; // Will be retried by the queue
}
logger.error(`Sync failed (no retry): ${err.message}`, { course });
}
}
Repita o pedido em caso de 5xx e 429. Registe e descarte erros 4xx (indicam um problema de dados, não uma falha transitória).