Synchronize activities
Goal: carry a class's calendar (lessons, shifts, practice sessions) from your system to Presente. There are two ways: CSV import (many activities at once, idempotent) and single create (one activity, with every field).
You need: CADASTRAR on the class (see Permissions) and the class id.
Import by CSV (recommended for calendars)
data,inicio,fim,descricao,local
10/03/2026,08:00,12:00,Emergency room shift,Example Hospital
11/03/2026,14:00,16:00,Basic techniques lesson,Lab 2| Column | Required | Format |
|---|---|---|
data | yes | YYYY-MM-DD or DD/MM/YYYY (aliases: date, fecha). |
inicio and fim | no, but together | HH:MM or HH:MM:SS; fim after inicio. Only one of the two filled in is an error. |
descricao | no | Up to 2000 characters. |
local | no | Up to 500 characters. |
Limits and separators: the same as Synchronize students (500 lines, 90,000 characters, comma, semicolon or tab).
Usage is the same as the student import: POST /turmas/{turmaId}/importacoes/atividades with { "csv": "...", "modo": "simular" }, check the report and repeat with "modo": "aplicar" (all or nothing; parcial with hashConteudo as described there).
curl -X POST "$API/turmas/$TURMA/importacoes/atividades" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d "$(jq -n --rawfile csv atividades.csv '{csv: $csv, modo: "aplicar"}')"Idempotency: an activity is recognized by the combination date + start time + description (case-insensitive). Reapplying the same file gives JA_EXISTE for the ones that already exist and CRIADA for new ones. But:
- The import only creates: it does not change or delete activities. If you change the time or description in your system, the import treats it as a new activity.
- Two identical lines in the file: the second is
ERRO(duplicate). - The
descricaotext becomes (or reuses, by name) an item of the discipline's activity catalog; always use the same spelling for the same activity. Fields the CSV does not have (mandatory, location fence, etc.) get default values; adjust them through the single create.
Single create
To control every field, or to change and cancel:
curl -X POST "$API/turmas/$TURMA/atividades" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"data":"2026-03-10","horaInicio":"08:00","horaFim":"12:00","descricao":"Emergency room shift","local":"Example Hospital","obrigatoria":true}'Only data is required. Other fields: atividadeProfissionalId (item of the discipline catalog), observacoes, obrigatoria, cancelada, cercaAtiva, cercaRaioM (10 to 5000 m), lat and lng. See the full model in AtividadeCriar.
Careful:
POST /turmas/{turmaId}/atividadesis not idempotent. If the call fails on the network after the server processed it, repeating it creates a duplicate activity. Before repeating after an uncertain failure, list the day's activities (GET /turmas/{turmaId}/atividades?de=2026-03-10&ate=2026-03-10) and check.
Read, change and remove
| Operation | Route |
|---|---|
List (with date filter de and ate) | GET /turmas/{turmaId}/atividades |
| Read one | GET /turmas/{turmaId}/atividades/{atividadeId} |
| Change | PATCH /turmas/{turmaId}/atividades/{atividadeId} |
| Cancel (stays on the calendar) | PATCH with {"cancelada": true} |
| Remove | DELETE /turmas/{turmaId}/atividades/{atividadeId} |
Removing is only possible while the activity has no attendance recorded; after that the response is 409 CONFLICT. In that case, cancel instead of removing.
A calendar synchronization flow
- Read the class's current activities (all pages) and build a map
date|start|description → id. - For each activity in your system: if it is in the map, compare the fields and use
PATCHif they changed; if it is not, create it (CSV or single). - For activities your system removed that exist in Presente: cancel (
PATCHwithcancelada: true) or remove (DELETE, if it has no attendance).
This flow avoids the CSV limitation (it only creates) and duplicates nothing.
Example (Python): create the missing ones
def key(a):
return (a["data"], (a["horaInicio"] or "")[:5], (a["descricao"] or "").lower())
existing = {key(a) for a in all_items(f"/turmas/{TURMA}/atividades", token)} # see "Pagination and limits"
for a in my_system_calendar:
if key(a) in existing:
continue
r = requests.post(f"{API}/turmas/{TURMA}/atividades",
headers={"Authorization": f"Bearer {token}"}, json=a)
r.raise_for_status()