Developers · API documentation

Reference

Complete reference of the EduPay API routes, with parameters, responses and curl examples.

Every API route, generated from the OpenAPI 1.0.0-beta specification. Examples use this server's address; keep the key in the EDUPAY_KEY variable.

Download openapi.json

Account

The school that owns the key.

GET /api/v1/me

The school that owns the key

Any key

Details of the school that owns the key and of the key itself. Any valid key can call it; use it to test the connection.

Example

curl https://edupay.estudiosite.com/api/v1/me \
  -H "Authorization: Bearer $EDUPAY_KEY"

Responses

200 The school and the key. application/json
{
    "data": {
        "school": {
            "name": "Colégio Modelo",
            "country": "BR",
            "currency": "BRL",
            "timezone": "America/Sao_Paulo"
        },
        "token": {
            "name": "ERP Financeiro",
            "prefix": "edp_7d2e9f01...",
            "scopes": [
                "students:read",
                "billing:read"
            ],
            "expires_at": null
        }
    }
}
401 Key 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"
}
403 API 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"
    ]
}
429 Rate 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.

Example

curl https://edupay.estudiosite.com/api/v1/openapi.json

Responses

200 The specification. application/json

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 with students:read

Parameters

NameTypeDescription
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.
external_idin query · optional string Student code in the school's system.
statusin query · optional string (active, inactive)

Example

curl https://edupay.estudiosite.com/api/v1/students \
  -H "Authorization: Bearer $EDUPAY_KEY"

Responses

200 Page of students. application/json
400 Invalid 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"
}
401 Key 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"
}
403 API 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"
    ]
}
422 Invalid 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."
        ]
    }
}
429 Rate 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 with students: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

NameTypeDescription
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)

NameTypeDescription
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").

Example

curl -X POST https://edupay.estudiosite.com/api/v1/students \
  -H "Authorization: Bearer $EDUPAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_id":"ALU-0091","full_name":"Maria Souza","email":"maria@exemplo.com","document_number":"12345678909","phone":"11999990000","address":{"zip_code":"01310-100","line":"Av. Paulista","number":"1000","neighborhood":"Bela Vista","city":"São Paulo","state":"SP","country":"BR"}}'

Responses

201 Student created. application/json
400 Idempotency-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"
}
401 Key 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"
}
403 API 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"
}
409 Another 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"
}
422 Invalid 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."
        ]
    }
}
429 Rate 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}

One student

Key with students:read

Parameters

NameTypeDescription
studentin path · required integer

Example

curl https://edupay.estudiosite.com/api/v1/students/123 \
  -H "Authorization: Bearer $EDUPAY_KEY"

Responses

200 The student. application/json
401 Key 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"
}
403 API 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"
    ]
}
404 The record does not exist in this school. application/problem+json
{
    "type": "about:blank",
    "title": "Not Found",
    "status": 404,
    "detail": "Registro não encontrado.",
    "code": "not_found"
}
429 Rate 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"
}
PATCH /api/v1/students/{student}

Update student

Key with students: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

NameTypeDescription
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)

NameTypeDescription
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.
birth_dateoptional string | null
statusoptional string (active, inactive)
addressoptional object

Example

curl -X PATCH https://edupay.estudiosite.com/api/v1/students/123 \
  -H "Authorization: Bearer $EDUPAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone":"11988887777","address":{"number":"1200"}}'

Responses

200 Student updated. application/json
400 Idempotency-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"
}
401 Key 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"
}
403 API 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"
    ]
}
404 The record does not exist in this school. application/problem+json
{
    "type": "about:blank",
    "title": "Not Found",
    "status": 404,
    "detail": "Registro não encontrado.",
    "code": "not_found"
}
409 Another 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"
}
422 Invalid 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."
        ]
    }
}
429 Rate 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 with billing: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.

Parameters

NameTypeDescription
studentin path · required integer

Example

curl https://edupay.estudiosite.com/api/v1/students/123/summary \
  -H "Authorization: Bearer $EDUPAY_KEY"

Responses

200 The summary. application/json
{
    "data": {
        "student_id": 532,
        "currency": "BRL",
        "open_installments": 3,
        "overdue_installments": 1,
        "amount_due": 135000,
        "amount_overdue": 45000,
        "next_installment": {
            "id": 9813,
            "due_date": "2026-11-10",
            "amount": 45000
        },
        "active_enrollments": 1,
        "contract_status": "signed"
    }
}
401 Key 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"
}
403 API 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"
    ]
}
404 The record does not exist in this school. application/problem+json
{
    "type": "about:blank",
    "title": "Not Found",
    "status": 404,
    "detail": "Registro não encontrado.",
    "code": "not_found"
}
429 Rate 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 with enrollments:read

Parameters

NameTypeDescription
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.
student_idin query · optional integer
external_idin query · optional string Enrollment code in the school's system.
offering_idin query · optional integer
trail_idin query · optional integer
cohort_idin query · optional integer
statusin query · optional string (pending_contract, pending_payment, active, cancelled, completed)

Example

curl https://edupay.estudiosite.com/api/v1/enrollments \
  -H "Authorization: Bearer $EDUPAY_KEY"

Responses

200 Page of enrollments. application/json
400 Invalid 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"
}
401 Key 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"
}
403 API 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"
    ]
}
422 Invalid 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."
        ]
    }
}
429 Rate 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 with enrollments: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

NameTypeDescription
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)

NameTypeDescription
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.
billing_start_dateoptional string | null Billing start date (YYYY-MM-DD).
billing_due_dayoptional integer | null Due day of the installments.

Example

curl -X POST https://edupay.estudiosite.com/api/v1/enrollments \
  -H "Authorization: Bearer $EDUPAY_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"external_id":"PED-2026-1187","student":{"external_id":"ALU-0091","full_name":"Maria Souza","email":"maria@exemplo.com","document_number":"12345678909","phone":"11999990000"},"offering_id":14,"cohort_id":3,"financial_plan_id":2,"payment_method":"pix"}'

Responses

201 Enrollment created. application/json
400 Idempotency-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"
}
401 Key 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"
}
403 API 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"
    ]
}
409 The 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
}
422 Invalid 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."
        ]
    }
}
429 Rate 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 with enrollments:read

Parameters

NameTypeDescription
enrollmentin path · required integer
includein query · optional string Related data, comma-separated: student (requires students:read), installments (requires billing:read), contract.

Example

curl https://edupay.estudiosite.com/api/v1/enrollments/123 \
  -H "Authorization: Bearer $EDUPAY_KEY"

Responses

200 The enrollment. application/json
401 Key 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"
}
403 API 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"
    ]
}
404 The record does not exist in this school. application/problem+json
{
    "type": "about:blank",
    "title": "Not Found",
    "status": 404,
    "detail": "Registro não encontrado.",
    "code": "not_found"
}
422 Invalid 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."
        ]
    }
}
429 Rate 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}/contract

Enrollment contract

Key with enrollments:read

Parameters

NameTypeDescription
enrollmentin path · required integer

Example

curl https://edupay.estudiosite.com/api/v1/enrollments/123/contract \
  -H "Authorization: Bearer $EDUPAY_KEY"

Responses

200 The contract. application/json
401 Key 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"
}
403 API 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"
    ]
}
404 The record does not exist in this school. application/problem+json
{
    "type": "about:blank",
    "title": "Not Found",
    "status": 404,
    "detail": "Registro não encontrado.",
    "code": "not_found"
}
429 Rate 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}/contract/pdf

Contract PDF

Key with enrollments:read

The same PDF that the school panel downloads.

Parameters

NameTypeDescription
enrollmentin path · required integer

Example

curl https://edupay.estudiosite.com/api/v1/enrollments/123/contract/pdf \
  -H "Authorization: Bearer $EDUPAY_KEY"

Responses

200 The file. application/pdf
401 Key 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"
}
403 API 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"
    ]
}
404 The record does not exist in this school. application/problem+json
{
    "type": "about:blank",
    "title": "Not Found",
    "status": 404,
    "detail": "Registro não encontrado.",
    "code": "not_found"
}
429 Rate 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/{enrollment}/cancel

Cancel enrollment

Key with enrollments: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

NameTypeDescription
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.

Body fields (JSON)

NameTypeDescription
reasonoptional string | null (student_withdrawal, transfer, default, administrative, null) Student withdrawal, transfer, default (non-payment) or administrative decision.

Example

curl -X POST https://edupay.estudiosite.com/api/v1/enrollments/123/cancel \
  -H "Authorization: Bearer $EDUPAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason":"student_withdrawal"}'

Responses

200 Enrollment cancelled. application/json
400 Idempotency-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"
}
401 Key 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"
}
403 API 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"
    ]
}
404 The record does not exist in this school. application/problem+json
{
    "type": "about:blank",
    "title": "Not Found",
    "status": 404,
    "detail": "Registro não encontrado.",
    "code": "not_found"
}
409 The 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"
}
422 Invalid 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."
        ]
    }
}
429 Rate 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 with billing:read

Parameters

NameTypeDescription
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).
enrollment_idin query · optional integer
student_idin query · optional integer
statusin query · optional string (open, paid, overdue, cancelled, waived, outstanding)
due_fromin query · optional string
due_toin query · optional string
paid_fromin query · optional string
paid_toin query · optional string

Example

curl https://edupay.estudiosite.com/api/v1/installments \
  -H "Authorization: Bearer $EDUPAY_KEY"

Responses

200 Page of installments. application/json
400 Invalid 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"
}
401 Key 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"
}
403 API 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"
    ]
}
422 Invalid 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."
        ]
    }
}
429 Rate 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 with billing:read

Includes the PIX QR Code image (payment.pix_qr_code_base64), which the list does not return.

Parameters

NameTypeDescription
installmentin path · required integer

Example

curl https://edupay.estudiosite.com/api/v1/installments/123 \
  -H "Authorization: Bearer $EDUPAY_KEY"

Responses

200 The installment. application/json
401 Key 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"
}
403 API 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"
    ]
}
404 The record does not exist in this school. application/problem+json
{
    "type": "about:blank",
    "title": "Not Found",
    "status": 404,
    "detail": "Registro não encontrado.",
    "code": "not_found"
}
429 Rate 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/installments/{installment}/second-copy

Issue second copy

Key with billing:write

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

NameTypeDescription
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)

NameTypeDescription
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.

Example

curl -X POST https://edupay.estudiosite.com/api/v1/installments/123/second-copy \
  -H "Authorization: Bearer $EDUPAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{"due_date":"2026-11-20"}'

Responses

201 The new installment, with the payment details. application/json
400 Idempotency-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"
}
401 Key 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"
}
403 API 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"
    ]
}
404 The record does not exist in this school. application/problem+json
{
    "type": "about:blank",
    "title": "Not Found",
    "status": 404,
    "detail": "Registro não encontrado.",
    "code": "not_found"
}
409 Installment 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"
}
422 Invalid 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."
        ]
    }
}
429 Rate 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"
}
502 Stripe 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.

GET /api/v1/webhooks

List webhook endpoints

Key with webhooks:manage

Example

curl https://edupay.estudiosite.com/api/v1/webhooks \
  -H "Authorization: Bearer $EDUPAY_KEY"

Responses

200 The school's endpoints (without the secret). application/json
401 Key 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"
}
403 API 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"
    ]
}
429 Rate 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 with webhooks: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)

NameTypeDescription
urloptional string Must be https and point to a public address.
descriptionoptional string | null
eventsoptional array Subscribed events (WebhookEventType) or ["*"] for all.

Example

curl -X POST https://edupay.estudiosite.com/api/v1/webhooks \
  -H "Authorization: Bearer $EDUPAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://erp.escola.com.br/edupay/avisos","description":"ERP financeiro","events":["installment.paid","installment.overdue"]}'

Responses

201 Endpoint created, with the secret. application/json
401 Key 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"
}
403 API 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"
    ]
}
409 Endpoint limit reached. application/problem+json
422 Invalid 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."
        ]
    }
}
429 Rate 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/webhooks/{webhook}

One webhook endpoint

Key with webhooks:manage

Parameters

NameTypeDescription
webhookin path · required integer

Example

curl https://edupay.estudiosite.com/api/v1/webhooks/123 \
  -H "Authorization: Bearer $EDUPAY_KEY"

Responses

200 The endpoint. application/json
401 Key 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"
}
403 API 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"
    ]
}
404 The record does not exist in this school. application/problem+json
{
    "type": "about:blank",
    "title": "Not Found",
    "status": 404,
    "detail": "Registro não encontrado.",
    "code": "not_found"
}
429 Rate 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"
}
PATCH /api/v1/webhooks/{webhook}

Update webhook endpoint

Key with webhooks:manage

Send only what changes. enabled: false pauses it; enabled: true turns it back on and resets the failure count.

Parameters

NameTypeDescription
webhookin path · required integer

Example

curl -X PATCH https://edupay.estudiosite.com/api/v1/webhooks/123 \
  -H "Authorization: Bearer $EDUPAY_KEY" \
  -H "Content-Type: application/json"

Responses

200 The updated endpoint. application/json
401 Key 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"
}
403 API 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"
    ]
}
404 The record does not exist in this school. application/problem+json
{
    "type": "about:blank",
    "title": "Not Found",
    "status": 404,
    "detail": "Registro não encontrado.",
    "code": "not_found"
}
422 Invalid 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."
        ]
    }
}
429 Rate 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"
}
DELETE /api/v1/webhooks/{webhook}

Delete webhook endpoint

Key with webhooks:manage

Also deletes the delivery history.

Parameters

NameTypeDescription
webhookin path · required integer

Example

curl -X DELETE https://edupay.estudiosite.com/api/v1/webhooks/123 \
  -H "Authorization: Bearer $EDUPAY_KEY"

Responses

204 Deleted.
401 Key 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"
}
403 API 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"
    ]
}
404 The record does not exist in this school. application/problem+json
{
    "type": "about:blank",
    "title": "Not Found",
    "status": 404,
    "detail": "Registro não encontrado.",
    "code": "not_found"
}
429 Rate 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/webhooks/{webhook}/deliveries

Endpoint delivery history

Key with webhooks:manage

Last 30 days, oldest first.

Parameters

NameTypeDescription
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).

Example

curl https://edupay.estudiosite.com/api/v1/webhooks/123/deliveries \
  -H "Authorization: Bearer $EDUPAY_KEY"

Responses

200 Page of deliveries. application/json
400 Invalid 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"
}
401 Key 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"
}
403 API 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"
    ]
}
404 The record does not exist in this school. application/problem+json
{
    "type": "about:blank",
    "title": "Not Found",
    "status": 404,
    "detail": "Registro não encontrado.",
    "code": "not_found"
}
429 Rate 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/{webhook}/test

Send test notification

Key with webhooks:manage

Sends a webhook.test notification to the endpoint, in a single attempt. Works even with the school's API turned off.

Parameters

NameTypeDescription
webhookin path · required integer

Example

curl -X POST https://edupay.estudiosite.com/api/v1/webhooks/123/test \
  -H "Authorization: Bearer $EDUPAY_KEY"

Responses

202 Test notification queued. application/json
401 Key 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"
}
403 API 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"
    ]
}
404 The record does not exist in this school. application/problem+json
{
    "type": "about:blank",
    "title": "Not Found",
    "status": 404,
    "detail": "Registro não encontrado.",
    "code": "not_found"
}
429 Rate 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"
}