Developers

Versioning and compatibility

API v3 evolves along four independent axes. A release can change one of them without touching the others, and you track each one through a different channel.

The four axes

AxisWhere it appearsChanges when
Major route version/api/v3 in the pathThe contract changes in a breaking way. v4 ships with its own OpenAPI document, and v3 keeps being served until the Sunset date of each operation
Contract releaseX-Cademi-Release on every response, info.version in the OpenAPI document, the release field of GET /capabilitiesEvery compatible release: a minor release adds an operation, field, or enum value; a patch release fixes text or documentation without changing the shape
OpenAPI document formatopenapi: 3.1.0 at the top of the documentThe OpenAPI specification version used to describe the API. It is not an API version
Event versionversion in the event envelope, next to typeThe shape of the event payload changes. A webhook destination pins payload_version and receives events in the version it subscribed to

Compatible changes

These changes fit within the same major version (3.x) and do not break existing integrations:

  • a new operation;
  • a new optional request field;
  • a new response field;
  • a new value in an enum marked x-extensible: true (treat unknown values as opaque);
  • a new filter, a new header, or relaxed validation;
  • a new error code under an already documented status.

Example: a resource response gains a new field. Clients that already read the JSON keep working, because nothing they received before is missing.

{
  "id": "prd_01J9X8Y7Z6A5B4C3D2E1F0G9H8",
  "object": "product",
  "name": "Curso de Marcenaria",
  "status": "published",
  "visibility": "public"
}

Breaking changes

These changes only ship in v4:

  • removing or renaming an operation, field, parameter, or enum value;
  • changing the type, format, or meaning of a field;
  • making an optional field required;
  • changing the HTTP status or the code of an already documented case;
  • changing the default ordering of a list;
  • lowering a limit (page size, batch size, upload size);
  • adding a value to an enum marked x-extensible: false.

Example: renaming name to title in the same response breaks every client that reads name. A change like this only exists under another major version, never within /api/v3:

{
  "id": "prd_01J9X8Y7Z6A5B4C3D2E1F0G9H8",
  "object": "product",
  "title": "Curso de Marcenaria",
  "status": "published"
}

Each enum declares its policy in the OpenAPI document. Closed enums (x-extensible: false) include state enums such as publication status, operation.status, and credential.status, as well as format and denial_reason. Open enums (x-extensible: true) include event type and operation type, and can gain new values without breaking your client.

Runtime deprecation

An operation scheduled for removal gets at least six months of notice before it stops responding:

  • the OpenAPI document marks the operation with deprecated: true, x-cademi-deprecated-at (the announcement date), and x-sunset (the cutoff date);
  • every response from that operation includes these headers:
Deprecation: @1798761600
Sunset: Thu, 01 Jul 2027 00:00:00 GMT
Link: <https://api.cademi.com.br/docs/deprecations>; rel="deprecation"
  • GET /capabilities lists deprecated operations in deprecations[]:
{
  "data": {
    "object": "capabilities",
    "release": "3.2.0",
    "deprecations": [
      {
        "operation_id": "products.legacy_export",
        "deprecated_at": "2027-01-01",
        "sunset_at": "2027-07-01"
      }
    ]
  }
}
  • between the announcement and the cutoff, the operation keeps responding normally;
  • after the Sunset date, the operation stops executing and returns 410 with the error code below.

Errors

CodeStatusWhen
operation_sunset410The operation was called after its announced Sunset date

No /api/v3 operation is deprecated at this time. This section describes the process that applies once one is.

Versioned events

Each release publishes, alongside the OpenAPI document, the catalog of public events at that point (type, version, visibility, and payload keys). A published event never changes shape without a new version: removing or renaming a field, or changing its visibility, produces a new version of the same type.

An event delivered to a webhook destination is always the JSON recorded when the event occurred, never rebuilt from the current payload shape. Replaying an older event (POST /webhooks/{id}/replays, GET /events/{id}) returns exactly what was recorded, in the version it was recorded in. Each webhook destination pins payload_version when you register it and receives events in that version. See Webhooks and Events.

Tracking changes

  • The changelog for each release describes what changed and what you need to do (nothing, for compatible changes).
  • The X-Cademi-Release header on any response tells you which release served the call.
  • GET /openapi.json always serves the document for the latest published release, never an earlier one.
  • GET /capabilities returns release and deprecations[], so your integration can track both programmatically without reading the changelog.

Version boundaries

API v3 runs on the same host as the legacy APIs (/api/v1, /api/v2, /api/third), but credentials from one are not valid on the other:

  • a v3 credential secret (ck_live_…/ck_test_…) sent to /api/v1, /api/v2, or /api/third is rejected with 401;
  • a legacy authentication token sent to /api/v3 is rejected with 401 unauthenticated;
  • a signed-in dashboard session does not authenticate requests to /api/v3. Calls to v3 require an API credential or an administrator's OAuth access token. See Authentication.

Compatibility matrix

One row per published v3 release. The CLI column is the first cademi CLI version that supports the release: CLI 0.1.0 (September 25, 2026, generated from 3.4.4) works with every 3.x release, and cademi doctor warns when the server runs a newer minor (its new operations are still reachable through cademi api) or a different major. Up to 3.3.2 each release only added. 3.4.0 corrects behavior that did not match the documentation, including a few changes that are not backward compatible, 3.12.0 replaces the score.removed event with score.deleted, 3.13.0 requires the release of the first import of an account and changes the status code of three responses, 3.14.0 moves the permissions of administrators to the policy format, and 3.15.0 lets an administrator call the API with the OAuth access token alone and retires the combined mode of access token and credential secret, 3.15.2 ends the transition that still accepted the access token of cademi-cli 0.3.x together with a credential secret, 3.15.3 makes the resource_id filter of GET /events take the public ID of the resource and require resource_type, and 3.16.0 removes the anonymous block from support tickets. All are listed in the changelog.

ReleaseDateWhat it addedCLI
3.0.0September 22, 2026Contract foundation: credentials and access policy, the HTTP request pipeline and base resources, the OpenAPI document, idempotency and concurrency control, asynchronous batch operations, file uploads, public events, auditing and usage limits, sandbox and reproducible scenarios0.1.0 or later
3.1.0September 22, 2026Showcases, banners, and navigation; products and modules; lessons, content, and taxonomies; users, tags, and custom fields; appearance, security, email, and profile settings; administrators and credential governance; webhooks (destinations, deliveries, and replays)0.1.0 or later
3.2.0September 23, 2026Enrollments, release rules, and progress; exams, questions, and results; certificates and legal terms; support tickets; sales, deliveries, and gateway integrations; gamification, points, and rankings; funnels, triggers, and automations; account domains and replicas; imports, exports, and reports; validation plans and batch application0.1.0 or later
3.3.0September 23, 2026No new operations. Replica behavior: publishing a product or module through the API also publishes its copies in replicated accounts; replicated products, modules, showcases, and banners accept status in PATCH; copies from or to a replica, new modules in a replicated product, and lessons moved to a replicated product return 403 replica_readonly; the showcase deletion description was corrected (the showcase's products are deleted with it).0.1.0 or later
3.3.1September 24, 2026No new operations. The OpenAPI document groups its tags with the x-tagGroups extension, used by documentation tools to organize the navigation.0.1.0 or later
3.3.2September 24, 2026No new operations. The Sales, Account, and Settings tags are split into smaller resource tags, and x-tagGroups gains separate Account and Settings groups. Operation IDs and paths are unchanged.0.1.0 or later
3.4.0September 25, 2026No new operations. Credential scope enforced on every documented route; cursor pagination and sorting fixed on several lists; fields that were accepted and ignored now take effect or are rejected; declarative configuration applies all nine resource kinds; per-group settings revisions; RateLimit-* headers; Idempotency-Key, 429, and common headers declared on every operation. See the changelog for the breaking changes.0.1.0 or later
3.4.1September 25, 2026No new operations. The OpenAPI document is valid for strict validators and client generators: the params fields of release rules and of the release calendar are declared as objects, and the OAuth scopes is an empty object instead of an empty array.0.1.0 or later
3.4.2September 25, 2026No new operations. The document is complete for typed client generation: every success response declares its body, every parameter has a description, untyped properties are typed as objects, and the event stream response is declared as text/event-stream. product_id[] joins the array filters of the rankings.0.1.0 or later
3.4.3September 25, 2026No new operations. details of unknown_field and unknown_parameter carry code and message; validation messages are translated; POST /event-streams accepts start (earliest or now).0.1.0 or later
3.4.4September 25, 2026No new operations. Fixes from an end-to-end sweep: several 500 responses replaced by success or documented errors, atomic ticket updates, per-provider security settings validation, own messages for every 4xx, import error codes, webhook attempt id, and richer sandbox test data.0.1.0 or later
3.4.5September 25, 2026No new operations. Specific error codes and translated messages everywhere: several 500 responses replaced by documented errors, domain rejections with codes and reasons, OperationError for operation items, Idempotency-Key checked per resource, If-Match validation, and common error responses declared.0.1.0 or later
3.4.6September 25, 2026No new operations. Lesson body writes no longer overwrite each other, operation errors always carry a message, invalid for malformed IDs in the body, and idempotency_key_reused tells a different resource from a different body.0.1.0 or later
3.4.7September 25, 2026No new operations. If-Match on lesson attachments checked atomically with the write, lesson revisions that change on every write, and clearer messages for malformed IDs and emails in use.0.1.0 or later
3.5.0September 28, 2026No new operations. Uploads are checked against a fixed list of extensions and a maximum size for each purpose; video is not accepted for any purpose. A file uploaded through the API must be used by a resource within 24 hours of the upload completing, or it is deleted; File gains expires_at. Each account has a storage quota, and an upload that would exceed it returns 409 quota_exceeded; GET /account/usage gains a storage object.0.1.0 or later
3.6.0September 28, 2026No new operations. Five learning-progress events (lesson and product completion, milestones, and progress updates); webhook delivery changes: a 429 no longer counts as an attempt, a failing URL gets its own delivery circuit reported in health.circuit_state, and timeout_ms now applies; endpoints with repeated delivery failures are deactivated automatically and can be reactivated with PATCH; the IDs of Diamond triggers change with this release (match them by stage_id).0.1.0 or later
3.6.1September 29, 2026No new operations. resource_filters accepts public IDs and declares its shape, a PATCH of a webhook with an http://localhost URL returns 422, and the retention of webhook deliveries is documented.0.1.0 or later
3.6.2September 29, 2026No new operations. The description of resource_filters is corrected: each key is the resource type of an event type, as listed in the event catalog, not the resource.object of the event in GET /events. Behavior did not change.0.1.0 or later
3.7.0September 29, 2026Payload detail levels for webhook endpoints (payload_detail: ids, masked, or full), with an expanded object alongside data for masked and full; a typed schema for every public event in the OpenAPI document. In diamond_membership.run_failed, error is now the code stage_run_failed instead of a technical message. validation_url of a certificate always points to the default address of the account.0.1.0 or later
3.8.0September 30, 2026No new operations. Four events: comment.created, question.created, user.custom_field_updated, and user.first_accessed. user.tags_updated is sent whenever the tags of a user change, including changes made by a delivery, a sale, or an import, and only then.0.1.0 or later
3.9.0September 30, 2026No new operations. Webhook endpoints accept event_filters, filters on fields of the event for each subscribed event type. A test delivery sent from the dashboard carries test: true. resource.id in GET /events, GET /events/{event_id}, and event streams is always the public ID of the resource, fixing event types that returned an internal number.0.1.0 or later
3.9.1October 1, 2026No new operations. enrollment.updated is also sent when a sales platform suspends or reactivates an enrollment, and the description of test in the webhook envelope names the level of detail a test delivery uses.0.1.0 or later
3.10.0October 3, 2026No new operations. The content of products and modules accepts limit (default 200) and cursor, and returns next_cursor past 200 entries; ticket replies declare limit, cursor, and 400 invalid_cursor, paging by creation date and then ID. Reorder operations return the whole reordered collection. Request bodies of settings updates, banner and menu item updates, email template previews and test messages, user attribution, and the creation of products, custom code blocks, and custom fields are documented field by field; If-Match is declared on the update and delete operations that honor it; PATCH /settings/custom-code/{custom_code_id} carries x-cademi-manual-checks. attachments[].url in ticket replies is a temporary link.0.1.0 or later
3.10.1October 6, 2026No new operations. Documentation only: limit declares its default of 50 on six lists; reorder operations declare that ids needs at least one item and explain how to build the complete set; access and deleted of GET /users, custom_fields in POST /users, and settings in PATCH /users/{user_id} are described; storage.used_bytes is a stored total recalculated daily.0.1.0 or later
3.10.2October 6, 2026No new operations. x-cademi-permissions entries can carry any_of: entries with the same value are alternatives, first used by GET /reports/rankings. In the body of POST /sales/deliveries, products has its own input schema, an object with entries, the same shape as the body of PUT /sales/deliveries/{delivery_id}/products. admin_two_factor_available of the authentication settings is deprecated and always true. Descriptions explain deleted in PATCH /sales/deliveries/{delivery_id} and that 403 sandbox_only and 409 sandbox_required both ask for a sandbox credential.0.1.0 or later
3.11.0October 6, 2026No new operations. Departments carry admin_id, and DELETE /support/departments/{department_id} accepts unlink_tickets=true. Tickets carry queue, also a filter of GET /support/tickets. A ticket reply can carry only attachments. GET /support/comments also lists pending replies, which carry comment_id, and a student reply can be deleted individually. Release rule params carry closes_after_days, unit, and closes_on, and exam_id is the public exam ID (exm_). Changes to the products and access schedules of a delivery apply to existing enrollments, except individual overrides. duration: null in PATCH /users/{user_id}/products/{product_id}/access inherits the delivery duration again.0.1.0 or later
3.12.0October 6, 2026DELETE /users/{user_id}/scores/{score_id} deletes a manual point entry with a required reason, needs the new scores.delete permission, and returns 422 score_not_removable for points earned automatically. score.removed is replaced by score.deleted, published once per deleted point entry with id, product_id, and adjustment_id; this is not backward compatible for webhook destinations and event streams subscribed to score.removed, which the platform moves to score.deleted. Points added in the dashboard publish score.adjusted. GET /support/comments declares its items as oneOf Comment or CommentReply, told apart by object.0.1.0 or later
3.12.1October 6, 2026No new operations. ProgressAdjustment.type lists its five values (reset, exam_result_deleted, exam_attempt_reopened, score_adjusted, and score_deleted) and is extensible, and result documents the keys of each type. In score_adjusted and score_deleted adjustments, result.score_id and result.product_id are public IDs (sco_, prd_) instead of internal numbers. The score.deleted example shows a manual deletion with exam_id set to null. scores.delete reports introduced_in 3.12.0 in GET /credentials/permission-catalog, and catalog_version moves to 3.12.0.0.1.0 or later
3.12.2October 7, 2026No new operations. The document declares fields and responses the API already returned: POST /event-streams declares its 429 as RateLimitError or the standard error (too_many_streams uses the standard one), every operation declares 403 (admin_not_eligible, tenant_mismatch) and 503, and UserAttribution, links.self of support and enrollment items, and supported_kinds of ValidationFailed are listed. A ticket without a user returns user as null instead of omitting it.0.1.0 or later
3.13.0October 8, 2026No new operations. The first import of an account is released by the Cademí team: until then, processing it with mode=initial returns 409 feature_disabled with details[].reason set to first_import_requires_release. GET /webhooks/{webhook_id} returns 404 for a deleted webhook endpoint; a credential granted administrator permissions on specific administrators can update, delete, restore, change the permissions of, and send access to them; archiving an archived delivery or reactivating an active one returns 200. Exports and import analyses no longer fail, filters.product_id and filters.tag_id of POST /exports accept a list, empty object fields are returned as {} instead of [], and a Diamond with memberships returns the reason has_memberships on deletion.0.1.0 or later
3.13.1October 9, 2026No new operations. Documentation only: attributes of a manifest item takes product_id for module and lesson, and module_id and content for lesson; the validation report lists the manifest's own error codes and warnings is always empty; the 409 of POST /users/{user_id}/certificates lists its reasons; summary of GET /reports/exports declares total and by_status; the limit of active event streams (5 per credential, 20 per account) is documented.0.1.0 or later
3.13.2October 9, 2026No new operations. summary.by_status of GET /reports/exports is returned as {} on an account without exports, as the document declares.0.1.0 or later
3.14.0October 12, 2026No new operations. The permissions of administrators use the policy format of credentials: GET /administrators/{administrator_id}/permissions returns role, full_access, and policies, PUT takes {role, policies} with the ETag of the permissions in If-Match, and POST /administrators takes policies instead of permissions; these changes are not backward compatible. Nobody grants more than they have (403 delegation_limit_exceeded), nobody changes their own permissions (409 state_conflict, reason self_modification), and published_by of a Policy can have kind system. The permission catalog lists five dashboard permissions that no operation requires, and catalog_version moves to 3.14.0.0.1.0 or later
3.15.0October 13, 2026No new operations. An administrator's OAuth access token authenticates alone and acts with the administrator's own permissions; together with a credential secret it returns 401 ambiguous_credentials. Credentials no longer carry auth_mode or human, POST /credentials with auth_mode returns 422 unknown_field, and GET /credentials/current and GET /credentials/current/policies return 404 with an administrator's access token; these changes are not backward compatible. human_context_required, human_mode_not_allowed, admin_not_eligible, and tenant_mismatch are no longer returned. delegation_limit_exceeded also applies to administrators, and requested_by.kind of a processing attempt can be administrator.0.1.0 or later; 0.4.0 to sign in without a credential secret
3.15.1October 14, 2026No new operations. Documentation only: the API description, DELETE /administrators/{administrator_id}, actor.admin_id of AuditEntry and RequestLog, and Capabilities describe the current model, where an administrator's OAuth access token alone acts as that administrator and a credential secret alone acts with the credential's policies.0.1.0 or later; 0.4.0 to sign in without a credential secret
3.15.2October 14, 2026No new operations. The transition of 3.15.0 ends: the access token of cademi auth login sent together with X-API-Key, as cademi-cli 0.3.x does, returns 401 ambiguous_credentials.0.1.0 or later with a credential secret; 0.4.0 to sign in as an administrator
3.15.3October 16, 2026No new operations. The resource_id filter of GET /events takes the public ID of the resource, the same value an event returns in resource.id (such as les_30), and requires resource_type; without it, or with a value that is not a public ID of that resource type, the request returns 422 validation_failed. This change is not backward compatible. PATCH /banners/{banner_id} keeps the visual settings of a netflix banner.0.1.0 or later with a credential secret; 0.4.0 to sign in as an administrator
3.15.4October 18, 2026No new operations. GET /gamification/scores no longer lists the entries of deleted users, and cause.lesson_id of comment and question entries is returned only when the entry references the lesson.0.1.0 or later with a credential secret; 0.4.0 to sign in as an administrator
3.16.0October 19, 2026No new operations. anonymous is no longer part of the Ticket schema, in the support ticket operations and in the expanded ticket of webhooks. No response ever returned the block.0.1.0 or later with a credential secret; 0.4.0 to sign in as an administrator
3.17.0October 21, 2026No new operations. timezone in GET /settings/platform and PATCH /settings/platform: the IANA time zone of the account, America/Sao_Paulo by default; null restores the default.0.1.0 or later with a credential secret; 0.4.0 to sign in as an administrator
3.18.0October 22, 2026No new operations. The configuration manifest accepts the email_template and custom_field kinds, so every setting that can be changed under /settings can also be changed through a manifest, and GET /credentials/policy-templates lists the settings_manager template.0.1.0 or later with a credential secret; 0.4.0 to sign in as an administrator
3.19.0October 25, 2026No new operations. label of POST /menus/{menu_key}/items and PATCH /menus/{menu_key}/items/{menu_item_id} accepts at most 50 characters (it was 155); a longer label returns 422 validation_failed on the label field. Menu items saved with a longer label keep it.0.1.0 or later with a credential secret; 0.4.0 to sign in as an administrator

On this page