JSON API
The public API is read-only. It needs no key and no cookies, and a key is never placed on the page. All addresses are under https://app.tempact.app/public/v1/.
Published content
Section titled “Published content”GET https://app.tempact.app/public/v1/companies/{slug}/content?location=all&lang=sr-Latn| Parameter | Default | Meaning |
|---|---|---|
location |
all |
A location ID or all locations |
lang |
the firm’s default language | Any language code, for example en, de or de-AT. The response is never a 404 because of the language. See below. |
Caching
Section titled “Caching”- The response has an
ETag. WithIf-None-Matchyou get304when nothing changed. Cache-Control: public, max-age=60, must-revalidate.X-Source-Validated-Atsays when the source was last checked.
Response (schema 1.0)
Section titled “Response (schema 1.0)”schemaVersion "1.0"revision stringpublicationVersion integer, increases with each publicationpublishedAt ISO vremecompany id, slug, name, currency ("RSD"), description, logo, logoAlt, email, phone, websitelocations[] id, name, city, address, contact, zone (IANA time zone), publicOpeningHours.days["1".."7"] = {open, close} or {closed: true}, publicExceptions[] = {date, open, close, closed, note}, hoursMismatch, hoursWarning, arrivalInstructionsservices[] id, location, name, priceMinor (integer, in minor units), currency, minorUnit (2), duration and buffer (minutes), kind ("main" | "addon"), category, variant, instructions, compatibleMainServiceIds[]staff[] id, name, location, serviceIds[], photo, photoAlt, descriptiontheme id, version, rendererKey ("minimal" | "barber" | "package"), packagemeta selectionRequired, availabilityIncluded (false), localePrices are in the smallest currency unit: priceMinor: 220000 with minorUnit: 2 means 2,200.00 RSD.
Images
Section titled “Images”The logo and team photos are at https://app.tempact.app/public/v1/media/{uuid}.webp.
Response language
Section titled “Response language”If the requested language does not exist, the server picks the first one that does: the same language in its general form (de-AT → de), then Serbian for bs, hr and cnr, then English, and finally the firm’s default language. The same applies to works?lang= and booking/catalog?lang=.
Field in meta |
Meaning |
|---|---|
locale |
The language actually delivered |
locales |
All languages the firm offers |
fallbackLocale |
The firm’s default language |
Portfolio
Section titled “Portfolio”GET https://app.tempact.app/public/v1/companies/{slug}/works?location=&worker=&category=The response is described on the Portfolio page.
Booking
Section titled “Booking”Free times and booking itself go through the form, not your code.
If the firm uses extra features, the form supports them by itself: number of people (people, and spotsLeft in free times), sessions (series: { index, total }) and deposit (deposit: { amountMinor, status, ipsQr }, where status is pending, paid or waived). The service duration in offered times is the duration for the chosen staff member. Use the link, dialog or inline form for booking. Your design can open the link with a location, staff member and service preselected from the data.
Refreshing the site after a publication
Section titled “Refreshing the site after a publication”The panel shows whether a connected site displays the latest publication. There are three ways.
1. Sections report on their own. With reportSeen: true the sections send the version they show.
2. A static site confirms its build. After each build the site sends:
POST https://app.tempact.app/public/v1/companies/{slug}/site-confirmContent-Type: application/json
{ "integrationId": "...", "publicationVersion": 12, "timestamp": 1793800000, "signature": "..." }signature is the HMAC-SHA256 of the integration secret over timestamp.integrationId.publicationVersion, as hex. The timestamp must be within ±300 seconds.
3. Webhook to your site. After each publication the system sends a POST to your site:
{ "event": "publication", "company": "your-business", "integrationId": "...", "publicationVersion": 12 }The X-Booking-Signature: t=TIMESTAMP,v1=SIGNATURE header holds the HMAC-SHA256 of the secret over TIMESTAMP.REQUEST_BODY. Verify the signature before starting a new build.
import { createHmac, timingSafeEqual } from 'node:crypto';
function verifySignature(header, body, secret) { const { t, v1 } = Object.fromEntries(header.split(',').map(d => d.split('='))); if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; const expected = createHmac('sha256', secret).update(`${t}.${body}`).digest('hex'); return timingSafeEqual(Buffer.from(expected), Buffer.from(v1));}You find the integration secret in the panel, under Website integration. Keep it on the server only, never in page code.