Docs

Content API reference

Schema version 4.1.0

casadigital.pt hosts a multi-tenant backoffice where customers manage the content of their own websites. Each customer website fetches its content through the authenticated Site API (documented at /api-docs); only casadigital.pt talks to the database.

This page documents the content formats: the normalized database collections and the per-resource shapes served by the Site API. It exposes field formats only, never content.

These collections are NOT directly accessible over HTTP. Writes happen exclusively through the casadigital.pt backoffice and its AI seeding tools; reads happen through the per-resource Site API endpoints. See /api-docs for the Site API.

Endpoints

Tenant model

Every tenant is one document in the sites collection. Its key field (the siteKey) identifies the tenant; consumer apps authenticate with the site's API key, which resolves to this document.

All other content documents carry a siteId field referencing the owning sites document. companies and siteSettings are one-per-site; legalTexts is one-per-document-key; services, products, caseStudies, locations and values are one-per-slug within a site; assets are one-per-pathname.

All documents also carry MongoDB _id, createdAt and updatedAt fields managed by the database layer; they are omitted from the tables below.

Contract scope

The platform owns design-agnostic business data only: business identity and contacts, opening hours, the services performed, the products sold, the case studies published, locations, company values, and the legal documents the client maintains. Every client website has its own bespoke design, so page structure, layout, section copy and metadata (band headings, stat figures, call-to-action labels, intro blocks) live in the client site's own repository and are deliberately absent from this contract.

Each resource maps to one collection: settings to companies, services/products/caseStudies/locations/values to their per-slug collections (array position stored in each document's order field), and legal to one legalTexts document per document key. Platform-only data never leaves: companies.availability is the schedule designer's working data (client sites read the schedule lines derived from it) and the whole siteSettings document holds backoffice preferences.

A service is work scoped per job and carries no price; a product is something sold at a stated price, stored as an amount plus its currency and billing period so each website can format it in its own style; a case study is work already delivered, told as problem, solution and results.

Resource responses omit siteId and every platform-only field, and they are fully defaulted: missing strings come back as empty strings and missing arrays as empty arrays. A site with no content yet returns valid empty resources rather than an error, so client sites can render an empty state.

Every uploaded file lives in the assets collection, stored in Vercel Blob through the backoffice and served at GET /api/v1/assets/{pathname}. Client sites typically proxy that under their own /api/blob/ route using the pathname. A service or a product embeds the pathname of its single image; a case study references whole asset documents, so its files arrive with their kind, MIME type, name, size and pixel dimensions resolved.

Site API resources

GET /api/v1/settings

The business: identity, contacts, opening hours and social links. One per site.

FieldTypeRequiredDescription
namestringnoBusiness name as displayed across the site.
taglinestringnoOne-line slogan shown near the logo/footer. Portuguese.
descriptionstringnoShort description of the business, 1-2 sentences. Portuguese.
phonestringnoDisplay phone number, formatted for reading (e.g. "+351 912 345 678"). Empty string if none. Derive a tel: URI from it by stripping everything but + and digits.
emailstringnoPublic contact email. Empty string if none.
whatsappstringnoWhatsApp chat URL (e.g. "https://wa.me/351912345678"). Empty string if none.
appUrlstringnoMobile app store URL if the business has an app. Empty string if none.
schedulearray of objectnoOpening schedule rows, in display order.
schedule[].daysstringnoDay range label (e.g. "Seg – Sáb"). Portuguese.
schedule[].hoursstringnoOpening hours label (e.g. "09h – 19h" or "Fechado").
socialobjectnoSocial media profile links.
social.facebookstringnoFacebook page URL. Empty string if none.
social.instagramstringnoInstagram profile URL. Empty string if none.
social.linkedinstringnoLinkedIn page URL. Empty string if none.
social.xstringnoX/Twitter profile URL. Empty string if none.
social.youtubestringnoYouTube channel URL. Empty string if none.

GET /api/v1/services

Services offered by the business, in display order.

FieldTypeRequiredDescription
[].slugstringyesURL-safe unique identifier, lowercase, hyphen-separated.
[].titlestringnoService name. Portuguese.
[].icon"truck" | "wrench" | "packageCheck" | "hammer" | "boxes" | "warehouse" | "arrowDownToLine" | "zap" | "clock" | "shieldCheck"yesIcon key from the fixed icon set (Lucide-style names).
[].imageobjectnoIllustrative image stored in Vercel Blob.
[].image.assetIdstringnoAsset document id, set by the backoffice after upload.
[].image.pathnamestringnoBlob storage pathname; client sites serve it through their own /api/blob/ proxy.
[].image.altstringnoImage alt text. Portuguese.
[].image.widthintegernoImage width in pixels.
[].image.heightintegernoImage height in pixels.
[].tier"primary" | "featured" | "secondary"yesDisplay tier: "primary" for the main highlighted services, "featured" for the secondary highlights, "secondary" for the rest.
[].shortstringnoOne-sentence summary shown on service cards. Portuguese.
[].descriptionstringnoFull description. Portuguese.
[].bulletsarray of stringnoBullet-point highlights. Portuguese.

GET /api/v1/products

Products sold by the business, in display order.

FieldTypeRequiredDescription
[].slugstringyesURL-safe unique identifier, lowercase, hyphen-separated.
[].titlestringnoProduct name. Portuguese.
[].icon"truck" | "wrench" | "packageCheck" | "hammer" | "boxes" | "warehouse" | "arrowDownToLine" | "zap" | "clock" | "shieldCheck"yesIcon key from the fixed icon set (Lucide-style names).
[].imageobjectnoIllustrative image stored in Vercel Blob.
[].image.assetIdstringnoAsset document id, set by the backoffice after upload.
[].image.pathnamestringnoBlob storage pathname; client sites serve it through their own /api/blob/ proxy.
[].image.altstringnoImage alt text. Portuguese.
[].image.widthintegernoImage width in pixels.
[].image.heightintegernoImage height in pixels.
[].skustringnoInternal reference or article number. Empty string if none.
[].pricenumbernoPrice amount in the currency below. 0 means the price is not published.
[].currency"EUR" | "USD" | "GBP"noCurrency of the price amount.
[].pricePeriod"once" | "month" | "year"noBilling period the price refers to: "once" for a one-off price, "month" or "year" for a subscription.
[].priceNotestringnoShort qualifier shown next to the price (e.g. "IVA incluído"). Portuguese.
[].shortstringnoOne-sentence summary shown on product cards. Portuguese.
[].descriptionstringnoFull description. Portuguese.
[].bulletsarray of stringnoBullet-point highlights. Portuguese.
[].featuredbooleannoTrue for a product the website should highlight.
[].availablebooleannoFalse for a product kept on record but not currently sold.

GET /api/v1/caseStudies

Case studies published by the business, in display order.

FieldTypeRequiredDescription
[].slugstringyesURL-safe unique identifier, lowercase, hyphen-separated.
[].clientstringnoName of the client the work was delivered for. Doubles as the case study's title.
[].problemstringnoThe situation the client was in. Portuguese.
[].solutionstringnoWhat the business did about it. Portuguese.
[].resultsstringnoWhat the client got out of it. Portuguese.
[].linksarray of objectnoRelated links, in display order.
[].links[].titlestringnoLink label. Portuguese. May be empty — fall back to showing the URL.
[].links[].urlstringnoAbsolute URL, including the scheme.
[].assetsarray of objectnoEvery attached file in one list, in display order. Group them by `kind` if your design shows media separately from documents.
[].assets[].kind"image" | "video" | "document"yesWhat the file is, derived from its MIME type: "image" and "video" are media, everything else is a "document". Split a list on this rather than on the file extension.
[].assets[].pathnamestringyesBlob storage pathname; fetch the file from GET /api/v1/assets/{pathname}, or proxy it through your own route.
[].assets[].contentTypestringnoMIME type the file was stored with (e.g. "image/webp", "application/pdf").
[].assets[].namestringnoOriginal filename at upload time. Use it as the download label for a document.
[].assets[].altstringnoAlt text, when one was written. Often empty — fall back to the surrounding context.
[].assets[].sizeintegernoFile size in bytes, when known.
[].assets[].widthintegernoIntrinsic width in pixels. Present for media, absent for documents.
[].assets[].heightintegernoIntrinsic height in pixels. Present for media, absent for documents.

GET /api/v1/locations

Physical locations of the business, in display order.

FieldTypeRequiredDescription
[].slugstringyesURL-safe unique identifier, lowercase, hyphen-separated.
[].citystringnoCity or locality name.
[].linesarray of stringnoAddress lines, in display order.
[].mapsSearchUrlstringnoGoogle Maps search/share URL for this address. Empty string if none.
[].mapEmbedUrlstringnoGoogle Maps embed URL (iframe src). Empty string if none.
[].primarybooleannoTrue for the main location (at most one per site).

GET /api/v1/values

Company values, in display order.

FieldTypeRequiredDescription
[].slugstringyesURL-safe unique identifier, lowercase, hyphen-separated.
[].titlestringnoValue name (e.g. Qualidade). Portuguese.
[].descriptionstringnoShort explanation of the value. Portuguese.

GET /api/v1/legal

Legal documents the client maintains, keyed by document. Only documents that have been written are present.

FieldTypeRequiredDescription
<key>objectnoOne legal document.
<key>.sectionsarray of objectnoOrdered sections, as the document should read.
<key>.sections[].titlestringnoSection heading. Portuguese.
<key>.sections[].bodystringnoSection body prose; plain text, may contain multiple sentences. Portuguese.

Normalized collections

sites

sites collection: one document per tenant (client website). Sites have no lifecycle state — a client site always serves the latest content.

FieldTypeRequiredDescription
keystringyesUnique tenant slug (siteKey). Echoed in every Site API response envelope; the tenant itself is selected by the API key, not by this value.
namestringyesBusiness name.
domainstringnoProduction domain of the client site.

companies

companies collection: the business itself, one document per site (unique siteId).

FieldTypeRequiredDescription
siteIdstringyesTenant scope: ObjectId of the owning document in the sites collection.
namestringyesBusiness name as displayed across the site.
taglinestringnoOne-line slogan. Portuguese.
descriptionstringnoShort business description. Portuguese.
phonestringnoDisplay phone number, formatted for reading.
emailstringnoPublic contact email.
whatsappstringnoWhatsApp chat URL.
appUrlstringnoMobile app store URL.
socialobjectnoSocial media profile links.
social.facebookstringnoFacebook page URL.
social.instagramstringnoInstagram profile URL.
social.linkedinstringnoLinkedIn page URL.
social.xstringnoX/Twitter profile URL.
social.youtubestringnoYouTube channel URL.
availabilityarray of objectnoStructured weekly opening hours from the backoffice designer. Never served to client sites — they read the derived schedule below.
availability[].idstringyesStable slot identifier.
availability[].week_dayintegeryesDay of week, 0 = Sunday.
availability[].start_timestringyesStart time as "HH:mm".
availability[].end_timestringyesEnd time as "HH:mm".
availability[].activebooleannoFalse for a slot kept but not in effect.
schedulearray of objectnoDisplay lines derived from availability on save, in display order.
schedule[].daysstringyesDay range label (e.g. "Segunda – Sábado").
schedule[].hoursstringyesOpening hours label (e.g. "09:00 – 19:00").

siteSettings

siteSettings collection: backoffice preferences, one document per site (unique siteId).

FieldTypeRequiredDescription
siteIdstringyesTenant scope: ObjectId of the owning document in the sites collection.
crmobjectnoBackoffice notification preferences. Never served to client sites.
crm.smsOnNewLeadbooleannoSend an SMS when a lead arrives.
crm.notificationsPhonestringnoNumber to notify.

services

services collection: one document per service (unique siteId+slug).

FieldTypeRequiredDescription
siteIdstringyesTenant scope: ObjectId of the owning document in the sites collection.
slugstringyesURL-safe identifier, unique per site.
titlestringyesService name. Portuguese.
icon"truck" | "wrench" | "packageCheck" | "hammer" | "boxes" | "warehouse" | "arrowDownToLine" | "zap" | "clock" | "shieldCheck"yesIcon key from the fixed icon set.
imageobjectnoOptional illustrative image stored in Vercel Blob.
image.assetIdstringnoReference to the assets collection.
image.pathnamestringnoBlob storage pathname; client sites serve it via their /api/blob/ proxy.
image.altstringnoImage alt text.
image.widthintegernoImage width in pixels.
image.heightintegernoImage height in pixels.
tier"primary" | "featured" | "secondary"yesDisplay tier: "primary", "featured", or "secondary".
shortstringnoOne-sentence summary. Portuguese.
descriptionstringnoFull description. Portuguese.
bulletsarray of stringnoBullet-point highlights. Portuguese.
orderintegernoSort index, ascending.

products

products collection: one document per product (unique siteId+slug).

FieldTypeRequiredDescription
siteIdstringyesTenant scope: ObjectId of the owning document in the sites collection.
slugstringyesURL-safe identifier, unique per site.
titlestringyesProduct name. Portuguese.
icon"truck" | "wrench" | "packageCheck" | "hammer" | "boxes" | "warehouse" | "arrowDownToLine" | "zap" | "clock" | "shieldCheck"yesIcon key from the fixed icon set.
imageobjectnoOptional illustrative image stored in Vercel Blob.
image.assetIdstringnoReference to the assets collection.
image.pathnamestringnoBlob storage pathname; client sites serve it via their /api/blob/ proxy.
image.altstringnoImage alt text.
image.widthintegernoImage width in pixels.
image.heightintegernoImage height in pixels.
skustringnoInternal reference or article number.
pricenumbernoPrice amount; 0 when not published.
currency"EUR" | "USD" | "GBP"yesCurrency of the price amount.
pricePeriod"once" | "month" | "year"yesBilling period: "once", "month" or "year".
priceNotestringnoShort qualifier shown next to the price.
shortstringnoOne-sentence summary. Portuguese.
descriptionstringnoFull description. Portuguese.
bulletsarray of stringnoBullet-point highlights. Portuguese.
featuredbooleannoTrue for a product the website should highlight.
availablebooleannoFalse for a product not currently sold.
orderintegernoSort index, ascending.

caseStudies

caseStudies collection: one document per case study (unique siteId+slug).

FieldTypeRequiredDescription
siteIdstringyesTenant scope: ObjectId of the owning document in the sites collection.
slugstringyesURL-safe identifier, unique per site.
clientstringyesName of the client the work was delivered for.
problemstringnoThe situation the client was in. Portuguese.
solutionstringnoWhat the business did about it. Portuguese.
resultsstringnoWhat the client got out of it. Portuguese.
linksarray of objectnoRelated links, in display order.
links[].titlestringnoLink label. Portuguese.
links[].urlstringnoAbsolute URL.
assetsarray of stringnoReferences into the assets collection, in display order. One list for media and documents alike; the Site API resolves each reference and reports its kind.
orderintegernoSort index, ascending.

locations

locations collection: one document per location (unique siteId+slug).

FieldTypeRequiredDescription
siteIdstringyesTenant scope: ObjectId of the owning document in the sites collection.
slugstringyesURL-safe identifier, unique per site.
citystringyesCity or locality name.
linesarray of stringyesAddress lines, in display order.
mapsSearchUrlstringnoGoogle Maps search/share URL.
mapEmbedUrlstringnoGoogle Maps embed URL (iframe src).
primarybooleannoTrue for the main location. At most one per site (enforced by a partial unique index).
orderintegernoSort index, ascending.

values

values collection: one document per company value (unique siteId+slug).

FieldTypeRequiredDescription
siteIdstringyesTenant scope: ObjectId of the owning document in the sites collection.
slugstringyesURL-safe identifier, unique per site.
titlestringyesValue name. Portuguese.
descriptionstringnoShort explanation. Portuguese.
orderintegernoSort index, ascending.

legalTexts

legalTexts collection: one document per site per legal document (unique siteId+key). The only page-shaped content the platform stores.

FieldTypeRequiredDescription
siteIdstringyesTenant scope: ObjectId of the owning document in the sites collection.
key"terms" | "privacy"yesWhich legal document. Unique per site.
sectionsarray of objectnoOrdered sections, as the document should read.
sections[].titlestringnoSection heading.
sections[].bodystringnoSection body prose, plain text.

assets

assets collection: files uploaded through the backoffice to Vercel Blob (unique siteId+pathname). Services and products embed the pathname of their single image; case studies reference whole documents by _id, which is how they can carry files of mixed kinds with their type and dimensions.

FieldTypeRequiredDescription
siteIdstringyesTenant scope: ObjectId of the owning document in the sites collection.
pathnamestringyesBlob storage pathname, unique per site. Client sites serve it via their /api/blob/ proxy.
urlstringnoDirect Blob URL.
originalNamestringnoOriginal filename at upload time.
contentTypestringnoMIME type.
sizeintegernoFile size in bytes.
widthintegernoImage width in pixels.
heightintegernoImage height in pixels.
blurDataURLstringnoBase64 blur placeholder for images.
altstringnoDefault alt text.
deletedAtstring | nullnoSoft-delete timestamp (ISO date), or null while active.