Platform
Organization
The business itself — address, contact details and opening hours — read without a key.
Every business has an address, a phone number and opening hours, whatever software it runs. This endpoint serves them. It is the one thing on a customer site that should never be typed into a template: the person who knows the address has changed is the person who can change it, and this is how that change reaches the site.
The endpoint
/api/client/v1/{organizationId}/organizationPublicBusiness details and opening hours. No key, any origin, cached for 60 seconds.
Keyless on purpose. This data is public — it is on the front door of the building — and a footer renders in the browser on every page, so it could not keep a key secret anyway. Editing these values needs a signed-in dashboard user; reading them does not.
{
"ok": true,
"contract": 1,
"data": {
"organization": {
"id": "0f2c…",
"name": "Trattoria Roma",
"timezone": "Europe/Amsterdam"
},
"profile": {
"legal_name": "Trattoria Roma B.V.",
"email": "hallo@trattoriaroma.nl",
"phone": "+31 20 123 4567",
"website": "https://trattoriaroma.nl",
"address": {
"street": "Dam", "house_number": "1", "address_line2": "2nd floor",
"postal_code": "1012JS", "city": "Amsterdam", "region": "Noord-Holland",
"country_code": "NL"
},
"social_links": { "instagram": "https://instagram.com/trattoriaroma" },
"legal": { "registration_number": "84100826", "vat_number": "NL001234567B01" }
},
"opening_hours": {
"timezone": "Europe/Amsterdam",
"weekly": {
"monday": { "enabled": true, "open": "09:00", "close": "17:00" },
"saturday": { "enabled": false, "open": "09:00", "close": "17:00" }
},
"exceptions": [ { "date": "2026-12-25", "mode": "closed", "label": "Christmas" } ],
"open_now": true,
"today": { "date": "2026-08-22", "enabled": true, "open": "09:00", "close": "17:00" }
}
}
}Absent means not shared
The whole profile object can be empty, and opening_hours can be null, for an organization that has filled in nothing. That is answered as a normal 200. Your footer still has to render — a page that throws because the owner has not got round to entering a phone number is a worse failure than a footer without one.
Which fields appear is the merchant's choice, made per field under Settings then Organization then General. Address, phone, email, socials and opening hours are published by default. The registration and VAT numbers are not: they are paperwork, not a shop window, so publishing them is an explicit decision. Code against the shape, never against what happens to be present today.
Putting it on the page
Three places want this data and they want different parts of it. A footer takes the name, address, phone, email and social links. A contact page takes the same plus the opening hours. Invoices and terms pages take legal.registration_number and legal.vat_number, and nothing else here.
address_line2 is free text the merchant types — "Unit 4", "2nd floor". Give it its own line, and drop the line entirely when it is absent rather than rendering an empty one.
country_code is ISO 3166-1 alpha-2: NL, BE, DE. It is a code, not a label. If a visitor should see a country name, map it yourself in their language — printing NL in a footer is the sort of detail that makes a site look unfinished.
const cnter = await window.cnter.ready
const { data } = await cnter.organization.get()
const profile = data?.profile ?? {}
const address = profile.address
<footer>
<p>{data?.organization?.name}</p>
{address && (
<p>
{[address.street, address.house_number].filter(Boolean).join(' ')}<br />
{[address.postal_code, address.city].filter(Boolean).join(' ')}
</p>
)}
{profile.phone && <a href={`tel:${profile.phone}`}>{profile.phone}</a>}
{profile.email && <a href={`mailto:${profile.email}`}>{profile.email}</a>}
{profile.legal?.vat_number && <p>VAT {profile.legal.vat_number}</p>}
</footer>Opening hours
A weekly schedule plus a list of exception dates. Resolving a given calendar date takes two steps, in this order.
- 01
Look for an exception on that date
Mode
closedmeans closed, whatever the weekday says. Modeopenuses that entry's ownopenandclose, whatever the weekday says. An exception always wins — that is what it is for: "we are normally closed on Sunday, but not this Sunday" has to be expressible. - 02
Otherwise use the weekday
Take
weekly[weekday]. Whenenabledisfalse, the venue is closed that day. Theopenandclosetimes are still there — a closed day keeps its times so reopening does not mean retyping them — so do not read them as opening hours.
open_now is computed server-side and is accurate to within the 60-second cache, which is fine for an "We're open" badge. If you need it exact — a button that must stop working one minute past closing — compute it yourself from weekly, exceptions and timezone. All three are in the response for exactly that reason.
Modules and their own hours
These are the organization's hours. A module may keep a narrower schedule of its own — a kitchen open until 23:00 that only accepts reservations until 21:00 — and in that case the module's own endpoint returns the resolved result. Read the hours from the module you are integrating, not from here, and do not try to combine the two yourself.
Exception dates work the other way round: a closure entered here applies to every module, including one running its own weekly schedule. A public holiday is a fact about the business, so the merchant enters it once. A module can still override a single date by adding its own entry for it.
Errors and limits
| Status | When | What to do |
|---|---|---|
400 | The organization id is not a uuid. | A configuration mistake. Check CNTER_ORG_ID. |
404 | No organization with that id. | Same — the id is wrong, not the request. |
429 | More than 60 requests a minute from one IP. | Cache the response instead of fetching it per component. |
The response carries Cache-Control: public, max-age=60. Respect it. A footer that fetches on every render will hit the limit on a busy page, and nothing here changes minute to minute.