401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
GET/api/v1/openapi.json
OpenAPI specification
No access key
This specification, in JSON (OpenAPI 3.1), with this server's address. Open, no key needed: use it to generate a client, import it into Postman or Insomnia and read it in AI tools.
200Page of offerings.application/json400Invalid cursor: restart the list without a cursor.application/problem+json
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Cursor inválido. Recomece a lista sem o parâmetro cursor.",
"code": "invalid_cursor"
}
401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
422Invalid parameter (per-field details in errors).application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Alguns campos estão inválidos.",
"code": "validation_failed",
"errors": {
"limit": [
"O campo limit não pode ser superior a 200."
]
}
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
200The offering.application/json401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
404The record does not exist in this school.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
GET/api/v1/trails
List tracks
Key withcatalog:read
Tracks with the offerings that make up each one, in the order the student takes them.
Parameters
Name
Type
Description
limitin query · optional
integer
Items per page (1 to 200).
cursorin query · optional
string
The meta.next_cursor of the previous page.
updated_sincein query · optional
string
Only records changed since this date and time (ISO 8601).
200Page of tracks.application/json400Invalid cursor: restart the list without a cursor.application/problem+json
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Cursor inválido. Recomece a lista sem o parâmetro cursor.",
"code": "invalid_cursor"
}
401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
422Invalid parameter (per-field details in errors).application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Alguns campos estão inválidos.",
"code": "validation_failed",
"errors": {
"limit": [
"O campo limit não pode ser superior a 200."
]
}
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
GET/api/v1/cohorts
List cohorts
Key withcatalog:read
Parameters
Name
Type
Description
limitin query · optional
integer
Items per page (1 to 200).
cursorin query · optional
string
The meta.next_cursor of the previous page.
updated_sincein query · optional
string
Only records changed since this date and time (ISO 8601).
200Page of cohorts.application/json400Invalid cursor: restart the list without a cursor.application/problem+json
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Cursor inválido. Recomece a lista sem o parâmetro cursor.",
"code": "invalid_cursor"
}
401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
422Invalid parameter (per-field details in errors).application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Alguns campos estão inválidos.",
"code": "validation_failed",
"errors": {
"limit": [
"O campo limit não pode ser superior a 200."
]
}
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
GET/api/v1/financial-plans
List payment plans
Key withcatalog:read
Accepted payment methods, number of installments, enrollment fee, interest and late fee. The plan's discount fields are not returned: they are not applied to charges today.
Parameters
Name
Type
Description
limitin query · optional
integer
Items per page (1 to 200).
cursorin query · optional
string
The meta.next_cursor of the previous page.
updated_sincein query · optional
string
Only records changed since this date and time (ISO 8601).
200Page of plans.application/json400Invalid cursor: restart the list without a cursor.application/problem+json
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Cursor inválido. Recomece a lista sem o parâmetro cursor.",
"code": "invalid_cursor"
}
401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
422Invalid parameter (per-field details in errors).application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Alguns campos estão inválidos.",
"code": "validation_failed",
"errors": {
"limit": [
"O campo limit não pode ser superior a 200."
]
}
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
GET/api/v1/sales-pages
List published sales pages
Key withcatalog:read
Only published pages, with the public link where prospects enroll and the price.
Parameters
Name
Type
Description
limitin query · optional
integer
Items per page (1 to 200).
cursorin query · optional
string
The meta.next_cursor of the previous page.
updated_sincein query · optional
string
Only records changed since this date and time (ISO 8601).
200Page of sales pages.application/json400Invalid cursor: restart the list without a cursor.application/problem+json
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Cursor inválido. Recomece a lista sem o parâmetro cursor.",
"code": "invalid_cursor"
}
401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
422Invalid parameter (per-field details in errors).application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Alguns campos estão inválidos.",
"code": "validation_failed",
"errors": {
"limit": [
"O campo limit não pode ser superior a 200."
]
}
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
Students
Student records (students:read, students:write), financial summary (billing:read) and portal sign-in link (portal:link).
GET/api/v1/students
List students
Key withstudents:read
Parameters
Name
Type
Description
limitin query · optional
integer
Items per page (1 to 200).
cursorin query · optional
string
The meta.next_cursor of the previous page.
updated_sincein query · optional
string
Only records changed since this date and time (ISO 8601).
emailin query · optional
string
Exact e-mail, case-insensitive.
document_numberin query · optional
string
CPF (Brazilian individual taxpayer ID) or other document, with or without punctuation.
200Page of students.application/json400Invalid cursor: restart the list without a cursor.application/problem+json
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Cursor inválido. Recomece a lista sem o parâmetro cursor.",
"code": "invalid_cursor"
}
401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
422Invalid parameter (per-field details in errors).application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Alguns campos estão inválidos.",
"code": "validation_failed",
"errors": {
"limit": [
"O campo limit não pode ser superior a 200."
]
}
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
POST/api/v1/students
Create student
Key withstudents:write
Creates the student and the portal account, like registration in the panel. The login follows the school's setting (CPF or e-mail).
Parameters
Name
Type
Description
Idempotency-Keyin header · optional
string
Unique code for this request (up to 255 visible characters; a UUID works). Repeating with the same key within 24 h returns the first response.
Body fields (JSON)
Name
Type
Description
external_idoptional
string | null
Student code in the school's system. Unique within the school.
full_namerequired
string
emailoptional
string | null
phoneoptional
string | null
document_numberoptional
string | null
CPF (checked when the school turns on validation). Unique within the school.
birth_dateoptional
string | null
statusoptional
string (active, inactive)
addressoptional
object
send_welcome_emailoptional
boolean
Sends the student the e-mail with the portal login and temporary password. With false, the account is created without the e-mail (use the sign-in link or "forgot password").
201Student created.application/json400Idempotency-Key missing where it is required (idempotency_key_required) or in the wrong format (idempotency_key_invalid).application/problem+json
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Este pedido exige o cabeçalho Idempotency-Key, com um código único por pedido (por exemplo, um UUID).",
"code": "idempotency_key_required"
}
401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off, key without the permission (insufficient_scope) or the plan's student limit reached (student_limit_reached).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "O plano da escola permite até 500 alunos, e o limite foi atingido.",
"code": "student_limit_reached"
}
409Another request with the same Idempotency-Key is still in progress.application/problem+json
{
"type": "about:blank",
"title": "Conflict",
"status": 409,
"detail": "Um pedido com esta Idempotency-Key ainda está em andamento. Aguarde alguns segundos e repita.",
"code": "idempotency_in_progress"
}
422Invalid fields (validation_failed, with per-field details in errors) or Idempotency-Key already used with different data (idempotency_key_reused).application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Alguns campos estão inválidos.",
"code": "validation_failed",
"errors": {
"address.line": [
"O campo endereço é obrigatório."
]
}
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
200The student.application/json401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
404The record does not exist in this school.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
PATCH/api/v1/students/{student}
Update student
Key withstudents:write
Changes only the fields sent. If the student did not have a portal account yet and the login data now exists, the account is created and the welcome e-mail goes out, like editing in the panel.
Parameters
Name
Type
Description
studentin path · required
integer
Idempotency-Keyin header · optional
string
Unique code for this request (up to 255 visible characters; a UUID works). Repeating with the same key within 24 h returns the first response.
Body fields (JSON)
Name
Type
Description
external_idoptional
string | null
Student code in the school's system. Unique within the school.
full_nameoptional
string
emailoptional
string | null
phoneoptional
string | null
document_numberoptional
string | null
CPF (checked when the school turns on validation). Unique within the school.
200Student updated.application/json400Idempotency-Key missing where it is required (idempotency_key_required) or in the wrong format (idempotency_key_invalid).application/problem+json
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Este pedido exige o cabeçalho Idempotency-Key, com um código único por pedido (por exemplo, um UUID).",
"code": "idempotency_key_required"
}
401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
404The record does not exist in this school.application/problem+json
409Another request with the same Idempotency-Key is still in progress.application/problem+json
{
"type": "about:blank",
"title": "Conflict",
"status": 409,
"detail": "Um pedido com esta Idempotency-Key ainda está em andamento. Aguarde alguns segundos e repita.",
"code": "idempotency_in_progress"
}
422Invalid fields (validation_failed, with per-field details in errors) or Idempotency-Key already used with different data (idempotency_key_reused).application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Alguns campos estão inválidos.",
"code": "validation_failed",
"errors": {
"address.line": [
"O campo endereço é obrigatório."
]
}
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
GET/api/v1/students/{student}/summary
Student financial summary
Key withbilling:read
Adds up the installments that can still be paid in any of the student's enrollments, including the fee from a cancelled enrollment. The contract is the one from the most recent live enrollment.
401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
404The record does not exist in this school.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
POST/api/v1/students/{student}/portal-link
Student portal sign-in link
Key withportal:link
Generates a link that opens the student portal already signed in, without a password. It is valid for 5 minutes and only once: generate it at the moment of the click. The student must be active and have an e-mail and a portal account.
Parameters
Name
Type
Description
studentin path · required
integer
Example
curl -X POST https://edupay.estudiosite.com/api/v1/students/123/portal-link \
-H "Authorization: Bearer $EDUPAY_KEY"
Responses
201Link generated.application/json401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
404The record does not exist in this school.application/problem+json
409Student inactive (student_inactive), without an e-mail or portal account (student_without_portal_access), portal without an address (portal_not_configured) or e-mail also used by another student (portal_link_ambiguous_email).application/problem+json
{
"type": "about:blank",
"title": "Conflict",
"status": 409,
"detail": "O aluno não tem e-mail ou conta de acesso ao portal.",
"code": "student_without_portal_access"
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
Enrollments
Enrollments and contracts (enrollments:read, enrollments:write).
GET/api/v1/enrollments
List enrollments
Key withenrollments:read
Parameters
Name
Type
Description
limitin query · optional
integer
Items per page (1 to 200).
cursorin query · optional
string
The meta.next_cursor of the previous page.
updated_sincein query · optional
string
Only records changed since this date and time (ISO 8601).
includein query · optional
string
Related data, comma-separated: student (requires students:read), installments (requires billing:read), contract.
200Page of enrollments.application/json400Invalid cursor: restart the list without a cursor.application/problem+json
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Cursor inválido. Recomece a lista sem o parâmetro cursor.",
"code": "invalid_cursor"
}
401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
422Invalid parameter (per-field details in errors).application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Alguns campos estão inválidos.",
"code": "validation_failed",
"errors": {
"limit": [
"O campo limit não pode ser superior a 200."
]
}
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
POST/api/v1/enrollments
Create enrollment
Key withenrollments:write
Creates the enrollment like the panel does: generates the installments and the contract, adds the student to Moodle right away (when the cohort is linked to Moodle) and issues the charges that are already inside the issuing window — card right away; boleto and PIX when the due date is a few days away. Requires Idempotency-Key. Accepts ?include= like the enrollment query.
Parameters
Name
Type
Description
Idempotency-Keyin header · required
string
Unique code for this request (up to 255 visible characters; a UUID works). Repeating with the same key within 24 h returns the first response.
includein query · optional
string
Related data, comma-separated: student (requires students:read), installments (requires billing:read), contract.
Body fields (JSON)
Name
Type
Description
external_idoptional
string | null
Enrollment code in the school's system. Unique within the school.
student_idoptional
integer
studentoptional
object
New student. Required fields follow the school's settings (CPF, phone and address may be required; e-mail is required when the student signs in with their e-mail).
offering_idoptional
integer
trail_idoptional
integer
cohort_idoptional
integer | null
Cohort. If it is linked to a Moodle cohort, the student joins Moodle right away, as with an enrollment made in the panel.
financial_plan_idrequired
integer
payment_methodrequired
string (boleto, pix, credit_card)
Must be accepted by the plan and by the school's Stripe account.
contract_template_idoptional
integer | null
Contract template. When a signature is required, the enrollment starts as awaiting contract.
base_valueoptional
integer | null
Course amount in cents. Without it, the course price applies (or the sum of the track's courses).
enrollment_feeoptional
integer | null
Enrollment fee in cents. Without it, the plan's fee applies.
201Enrollment created.application/json400Idempotency-Key missing where it is required (idempotency_key_required) or in the wrong format (idempotency_key_invalid).application/problem+json
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Este pedido exige o cabeçalho Idempotency-Key, com um código único por pedido (por exemplo, um UUID).",
"code": "idempotency_key_required"
}
401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off, key without the permission (insufficient_scope) or, with a new student, the plan's student limit reached (student_limit_reached).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: enrollments:write.",
"code": "insufficient_scope",
"scopes": [
"enrollments:write"
]
}
409The student already has a live enrollment in the same course or track (enrollment_conflict, with existing_enrollment_id) or the request with this Idempotency-Key is still in progress (idempotency_in_progress).application/problem+json
{
"type": "about:blank",
"title": "Conflict",
"status": 409,
"detail": "O aluno já tem matrícula viva neste curso ou trilha (veja existing_enrollment_id).",
"code": "enrollment_conflict",
"existing_enrollment_id": 812
}
422Invalid fields (validation_failed, with per-field details in errors) or Idempotency-Key already used with different data (idempotency_key_reused).application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Alguns campos estão inválidos.",
"code": "validation_failed",
"errors": {
"address.line": [
"O campo endereço é obrigatório."
]
}
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
GET/api/v1/enrollments/{enrollment}
One enrollment
Key withenrollments:read
Parameters
Name
Type
Description
enrollmentin path · required
integer
includein query · optional
string
Related data, comma-separated: student (requires students:read), installments (requires billing:read), contract.
200The enrollment.application/json401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
404The record does not exist in this school.application/problem+json
422Invalid parameter (per-field details in errors).application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Alguns campos estão inválidos.",
"code": "validation_failed",
"errors": {
"limit": [
"O campo limit não pode ser superior a 200."
]
}
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
200The contract.application/json401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
404The record does not exist in this school.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
200The file.application/pdf401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
404The record does not exist in this school.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
POST/api/v1/enrollments/{enrollment}/cancel
Cancel enrollment
Key withenrollments:write
Cancels like the panel, without a termination fee (that one only through the panel): open installments are cancelled along with their charges in Stripe, and the student is notified and removed from Moodle. A boleto that is still valid cannot be cancelled in Stripe and stays payable until it expires: meta.charges_still_payable says how many.
Parameters
Name
Type
Description
enrollmentin path · required
integer
Idempotency-Keyin header · optional
string
Unique code for this request (up to 255 visible characters; a UUID works). Repeating with the same key within 24 h returns the first response.
includein query · optional
string
Related data, comma-separated: student (requires students:read), installments (requires billing:read), contract.
200Enrollment cancelled.application/json400Idempotency-Key missing where it is required (idempotency_key_required) or in the wrong format (idempotency_key_invalid).application/problem+json
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Este pedido exige o cabeçalho Idempotency-Key, com um código único por pedido (por exemplo, um UUID).",
"code": "idempotency_key_required"
}
401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
404The record does not exist in this school.application/problem+json
409The enrollment is already cancelled or completed (enrollment_not_cancellable).application/problem+json
{
"type": "about:blank",
"title": "Conflict",
"status": 409,
"detail": "Só matrícula aguardando contrato, aguardando pagamento ou ativa pode ser cancelada.",
"code": "enrollment_not_cancellable"
}
422Invalid fields (validation_failed, with per-field details in errors) or Idempotency-Key already used with different data (idempotency_key_reused).application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Alguns campos estão inválidos.",
"code": "validation_failed",
"errors": {
"address.line": [
"O campo endereço é obrigatório."
]
}
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
Billing
Installments, payment details (billing:read) and second copy (billing:write).
GET/api/v1/installments
List installments
Key withbilling:read
Parameters
Name
Type
Description
limitin query · optional
integer
Items per page (1 to 200).
cursorin query · optional
string
The meta.next_cursor of the previous page.
updated_sincein query · optional
string
Only records changed since this date and time (ISO 8601).
200Page of installments.application/json400Invalid cursor: restart the list without a cursor.application/problem+json
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Cursor inválido. Recomece a lista sem o parâmetro cursor.",
"code": "invalid_cursor"
}
401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
422Invalid parameter (per-field details in errors).application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Alguns campos estão inválidos.",
"code": "validation_failed",
"errors": {
"limit": [
"O campo limit não pode ser superior a 200."
]
}
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
GET/api/v1/installments/{installment}
One installment
Key withbilling:read
Includes the PIX QR Code image (payment.pix_qr_code_base64), which the list does not return.
200The installment.application/json401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
404The record does not exist in this school.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
Issues the second copy with a new due date, like the panel: the current installment is cancelled along with its charge in Stripe and a new one is created (replaces_installment_id points to the previous one), with a new boleto or PIX and an e-mail to the student. The amount cannot be changed through the API.
Parameters
Name
Type
Description
installmentin path · required
integer
Idempotency-Keyin header · optional
string
Unique code for this request (up to 255 visible characters; a UUID works). Repeating with the same key within 24 h returns the first response.
Body fields (JSON)
Name
Type
Description
due_dateoptional
string | null
New due date (YYYY-MM-DD, today or later in the school's time zone). Without it, it is due in 3 days.
201The new installment, with the payment details.application/json400Idempotency-Key missing where it is required (idempotency_key_required) or in the wrong format (idempotency_key_invalid).application/problem+json
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Este pedido exige o cabeçalho Idempotency-Key, com um código único por pedido (por exemplo, um UUID).",
"code": "idempotency_key_required"
}
401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
404The record does not exist in this school.application/problem+json
409Installment paid, cancelled or waived (installment_not_payable), current boleto still valid (boleto_still_valid) or school without a connected Stripe account (gateway_not_connected).application/problem+json
{
"type": "about:blank",
"title": "Conflict",
"status": 409,
"detail": "O boleto atual ainda está dentro da validade e não pode ser cancelado.",
"code": "boleto_still_valid"
}
422Invalid fields (validation_failed, with per-field details in errors) or Idempotency-Key already used with different data (idempotency_key_reused).application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Alguns campos estão inválidos.",
"code": "validation_failed",
"errors": {
"address.line": [
"O campo endereço é obrigatório."
]
}
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
502Stripe declined or did not respond (gateway_error). With installment_id, the second copy was created and its charge is issued again automatically.application/problem+json
{
"type": "about:blank",
"title": "Bad Gateway",
"status": 502,
"detail": "O Stripe recusou ou não respondeu. Se veio installment_id, a 2ª via foi criada e a cobrança dela sai de novo automaticamente.",
"code": "gateway_error",
"installment_id": 4521
}
Webhooks
Endpoints that receive the school's webhooks (webhooks:manage). The notification format is under webhooks → evento.
200The school's endpoints (without the secret).application/json401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
POST/api/v1/webhooks
Create webhook endpoint
Key withwebhooks:manage
The response contains the secret (secret) only once: keep it to verify the signature of the notifications. Up to 10 endpoints per school.
Body fields (JSON)
Name
Type
Description
urloptional
string
Must be https and point to a public address.
descriptionoptional
string | null
eventsoptional
array
Subscribed events (WebhookEventType) or ["*"] for all.
201Endpoint created, with the secret.application/json401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
409Endpoint limit reached.application/problem+json422Invalid parameter (per-field details in errors).application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Alguns campos estão inválidos.",
"code": "validation_failed",
"errors": {
"limit": [
"O campo limit não pode ser superior a 200."
]
}
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
200The endpoint.application/json401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
404The record does not exist in this school.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
PATCH/api/v1/webhooks/{webhook}
Update webhook endpoint
Key withwebhooks:manage
Send only what changes. enabled: false pauses it; enabled: true turns it back on and resets the failure count.
200The updated endpoint.application/json401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
404The record does not exist in this school.application/problem+json
422Invalid parameter (per-field details in errors).application/problem+json
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "Alguns campos estão inválidos.",
"code": "validation_failed",
"errors": {
"limit": [
"O campo limit não pode ser superior a 200."
]
}
}
429Rate limit reached.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
204Deleted.401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
404The record does not exist in this school.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
GET/api/v1/webhooks/{webhook}/deliveries
Endpoint delivery history
Key withwebhooks:manage
Last 30 days, oldest first.
Parameters
Name
Type
Description
webhookin path · required
integer
limitin query · optional
integer
Items per page (1 to 200).
cursorin query · optional
string
The meta.next_cursor of the previous page.
updated_sincein query · optional
string
Only records changed since this date and time (ISO 8601).
200Page of deliveries.application/json400Invalid cursor: restart the list without a cursor.application/problem+json
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Cursor inválido. Recomece a lista sem o parâmetro cursor.",
"code": "invalid_cursor"
}
401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
404The record does not exist in this school.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
POST/api/v1/webhooks/{webhook}/test
Send test notification
Key withwebhooks:manage
Sends a webhook.test notification to the endpoint, in a single attempt. Works even with the school's API turned off.
Parameters
Name
Type
Description
webhookin path · required
integer
Example
curl -X POST https://edupay.estudiosite.com/api/v1/webhooks/123/test \
-H "Authorization: Bearer $EDUPAY_KEY"
Responses
202Test notification queued.application/json401Key missing, invalid, expired or revoked.application/problem+json
{
"type": "about:blank",
"title": "Unauthorized",
"status": 401,
"detail": "Chave de acesso ausente, inválida, vencida ou revogada.",
"code": "unauthenticated"
}
403API turned off for the school (code api_disabled) or key without the required permission (code insufficient_scope, with the list in scopes).application/problem+json
{
"type": "about:blank",
"title": "Forbidden",
"status": 403,
"detail": "A chave não tem a permissão necessária: students:read.",
"code": "insufficient_scope",
"scopes": [
"students:read"
]
}
404The record does not exist in this school.application/problem+json
{
"type": "about:blank",
"title": "Too Many Requests",
"status": 429,
"detail": "Limite de chamadas atingido. Tente de novo depois do tempo indicado em Retry-After.",
"code": "rate_limited"
}
Talk to us
Our team is available to answer questions about projects, consulting and educational technology.