zenly/Documentation
Search documentation
English
API referenceGetting started

Zenly API

A REST API over your own organization’s data, called with a key you mint. Use it to move records between Zenly and the systems Zenly is not: your website’s signup form, a spreadsheet, an accounting tool, a bot.

The API is in preview

Minting a key from inside Zenly is not shipped yet, so getting one is not self-service today. The surface below is the whole of it, and it is still growing.

Base URL and versioning

Every path lives under a version prefix, and the samples in this reference are written against https://api.zenly.app.br.

The prefix is a promise: we will add fields and endpoints under /v1, and we will not remove or repurpose what is already there. Treat unfamiliar fields in a response as something to ignore rather than something to fail on, since a new one is not a breaking change.

What the API can do today

The whole surface, straight from the document:

GET /v1/calendarSessions in a window

Every session between from and to, computed by expanding each schedule's recurrence rule. Enough to put a class timetable on your own site. It does not report who is booked: that is member data a timetable does not need.

GET /v1/clientsList clients

Newest first. locationIds is omitted on this endpoint; read a single client for its roster.

POST /v1/clientsCreate a client

Creates a client profile. The same rules the staff app enforces apply here: a CPF is checksum-validated and stored as 11 digits, and an id document already on another client is refused with 409.

GET /v1/clients/{id}Get a client

PUT /v1/clients/{id}Replace a client

A full replacement, not a patch: any field you omit is CLEARED. Read the client first and send it back with your changes applied.

DELETE /v1/clients/{id}Archive a client

Archiving is the delete this API offers. The profile stays, with status archived, because contracts, invoices and attendance all point at it.

GET /v1/contractsList contracts

What clients have bought. Filter by client to answer "what is this member on". Read a single contract for its live entitlement balances.

GET /v1/contracts/{id}Get a contract

GET /v1/locationsList locations

Every location the organization has, open or closed. Not paginated: the count is bounded by the subscription tier. These ids are what the rest of the API means by locationId.

GET /v1/organizationYour organization

Returns the organization the API key belongs to. The cheapest way to confirm a key works and to read the currency and timezone the rest of this API reports in.

GET /v1/plansList plans

What the organization sells, with the entitlements each grants. Enough to render a pricing page.

GET /v1/plans/{id}Get a plan

GET /v1/service-typesList service types

What the organization schedules: a class, a session, an appointment kind.

GET /v1/service-types/{id}Get a service type

Booking, invoicing, check-in and the contract lifecycle are not on the API. Anything that moves money or entitlements stays in Zenly itself for now.

Make your first call

  1. Have an owner mint a key. It is shown once, at mint time, and cannot be recovered afterwards.
  2. Put it somewhere your server can read it. A key can do anything an owner can do, so it belongs in server-side configuration and never in a browser or a mobile app.
  3. Send it as a bearer token. If you get a 200 back, you are done setting up.
cURL
export ZENLY_API_KEY=zk_...

curl https://api.zenly.app.br/v1/organization \
  -H "Authorization: Bearer $ZENLY_API_KEY"
Response200 OK
{
  "id": "019f9a89-62bf-750a-bdc0-375919dea60a",
  "name": "Iron District",
  "timezone": "America/Sao_Paulo",
  "currency": "BRL",
  "plan": "Avançado",
  "joinedAt": "2026-07-25T20:28:42.815055+02:00"
}

A 401 means the token did not resolve to a live key, and a 402 means the organization’s plan does not include the API. Errors covers the rest.

Conventions

JSON, camelCase

Requests and responses are JSON with camelCase field names. Every response also carries a $schema link, which you can ignore.

Ids are opaque UUIDs

UUIDv7 with no type prefix. They sort by creation time, but do not parse anything out of them: store and pass them as strings.

Times are RFC 3339 instants

Each timestamp carries an explicit UTC offset. Parse it as an instant and render it in the organization’s timezone, which is what its staff and members read the clock on.

Money is integer minor units

An amount is a count of the organization’s currency’s smallest unit, so R$ 159,00 is 15900. There are no decimals anywhere in this API.

One organization per key

The key decides which organization you are reading, so there is no organization parameter or header to send. A key never reaches another tenant’s data.

Generate a client instead

The contract is an OpenAPI 3.1 document, and pointing a generator at it beats hand-writing request code. OpenAPI has the document URLs, the generator commands and the schema links. None of it needs a key.