# Structured Data (JSON-LD) Audit — mnqcatering.com
**Audited:** 2026-09-15
**Method:** Source review (`src/layouts/MainLayout.astro`, `src/pages/*.astro`) cross-checked against the actual SSR output. Live `https://www.mnqcatering.com` was not directly reachable from this environment (no outbound DNS/network), so the site's own Node/Astro SSR server (`@astrojs/node`, `output: 'server'`) was built and run locally against the committed HEAD (`0e242a6`, working tree clean for `src/`), and pages were requested with `Host: www.mnqcatering.com` to reproduce byte-identical rendered HTML for `/`, `/bodas-reales`, `/corporativo-business`, `/reuniones-familiares`, `/contacto`. This is equivalent to fetching the live pages for structured-data purposes since output is fully server-rendered (no client-side JSON-LD injection to compare against — `raw` and `rendered` HTML are the same thing here).
## Summary
Every page emits one sitewide JSON-LD block (business identity) via `MainLayout.astro`, plus a page-specific `FAQPage` block on four pages. Both blocks are syntactically valid JSON, use `https://schema.org` as `@context`, and (for FAQPage) mirror the visible on-page FAQ content — no invisible/mismatched schema found. However, the sitewide block uses **`@type: "CateringService"`, which is not a valid schema.org type** — this is a critical defect that likely causes the whole block to be ignored or mis-typed by Google and other consumers. There's also a non-standard property (`serviceType`) riding on the invalid type. `BreadcrumbList`, `image`, `priceRange`, `geo`, and `sameAs` are all missing opportunities. FAQPage markup is correctly kept per the current no-rich-result policy (see below).
## Detection Results
| Page | Blocks found |
|---|---|
| `/` (home) | 1× `CateringService` (site-wide) |
| `/bodas-reales` | 1× `CateringService` + 1× `FAQPage` (3 Q&A) |
| `/corporativo-business` | 1× `CateringService` + 1× `FAQPage` (3 Q&A) |
| `/reuniones-familiares` | 1× `CateringService` + 1× `FAQPage` (3 Q&A) |
| `/contacto` | 1× `CateringService` + 1× `FAQPage` (3 Q&A) |
| `/nosotros`, `/aviso-legal`, `/privacidad`, `/cookies` | 1× `CateringService` only (no page-specific schema — expected, no FAQ content on these) |
Format: JSON-LD only (no Microdata/RDFa found), injected via `` in `MainLayout.astro:137`. Correctly server-rendered (present in raw HTML, not client-injected).
## Validation Results
### 1. Sitewide business block — `@type: "CateringService"` — **FAIL (Critical)**
**Evidence** (identical on every page, e.g. homepage):
```json
{
"@context": "https://schema.org",
"@type": "CateringService",
"name": "MNQ Catering y Evento",
"url": "https://www.mnqcatering.com",
"description": "Servicio de catering para bodas, eventos corporativos y reuniones familiares con enfoque premium.",
"telephone": "+34 678 17 15 13",
"serviceType": ["Bodas", "Eventos corporativos", "Reuniones familiares"],
"address": { "@type": "PostalAddress", "streetAddress": "Calle Camino Vivero, 6", "postalCode": "29014", "addressLocality": "Málaga", "addressRegion": "Málaga", "addressCountry": "ES" },
"areaServed": [{ "@type": "City", "name": "Málaga" }, { "@type": "AdministrativeArea", "name": "Provincia de Málaga" }],
"contactPoint": { "@type": "ContactPoint", "contactType": "customer service", "telephone": "+34 678 17 15 13", "availableLanguage": ["es"] }
}
```
**Problem:** `CateringService` does not exist in the schema.org vocabulary. The correct type for a catering business is **`CateringBusiness`**, a `LocalBusiness` → `FoodEstablishment` subtype (schema.org/CateringBusiness). Because `CateringService` isn't a recognized type, Google's Rich Results Test / structured data parsers will either flag it as an unrecognized type or silently drop type-specific interpretation, and any generic-type fallback loses `LocalBusiness` inheritance (`address`, `telephone`, `geo`, `openingHoursSpecification`, `priceRange`, `image` all become meaningless without a valid business type backing them).
A second, compounding issue: `serviceType` is a property of schema.org's `Service` type, not of `LocalBusiness`/`FoodEstablishment`/`CateringBusiness`. Even after fixing the `@type`, `serviceType` would still be an out-of-vocabulary property on this type. The three service lines (Bodas, Eventos corporativos, Reuniones familiares) are better modeled as `makesOffer` → `Offer.itemOffered` → `Service` entities, or dropped from the top-level entity and left for each landing page's own content/FAQ.
**Recommendation — fix `@type` to `CateringBusiness` and restructure services in `MainLayout.astro`:**
```json
{
"@context": "https://schema.org",
"@type": "CateringBusiness",
"name": "MNQ Catering y Evento",
"url": "https://www.mnqcatering.com",
"description": "Servicio de catering para bodas, eventos corporativos y reuniones familiares con enfoque premium.",
"telephone": "+34678171513",
"priceRange": "$$$",
"image": "https://www.mnqcatering.com/images/foto-bodas.jpg",
"address": {
"@type": "PostalAddress",
"streetAddress": "Calle Camino Vivero, 6",
"postalCode": "29014",
"addressLocality": "Málaga",
"addressRegion": "Málaga",
"addressCountry": "ES"
},
"areaServed": [
{ "@type": "City", "name": "Málaga" },
{ "@type": "AdministrativeArea", "name": "Provincia de Málaga" }
],
"contactPoint": {
"@type": "ContactPoint",
"contactType": "customer service",
"telephone": "+34678171513",
"availableLanguage": ["es"]
},
"makesOffer": [
{ "@type": "Offer", "itemOffered": { "@type": "Service", "name": "Catering para bodas" } },
{ "@type": "Offer", "itemOffered": { "@type": "Service", "name": "Catering para eventos corporativos" } },
{ "@type": "Offer", "itemOffered": { "@type": "Service", "name": "Catering para reuniones familiares" } }
]
}
```
Notes on this snippet:
- `telephone` normalized to E.164 (`+34678171513`, no spaces) — more consistently parsed than `"+34 678 17 15 13"` (minor, non-blocking, but worth fixing while editing this block anyway).
- `priceRange` is a placeholder pattern (`"$$$"`) — replace with a real value the business is comfortable publishing, or omit if there's no defensible public price band yet. Do **not** ship a placeholder string.
- `image` currently points at an existing OG image already used elsewhere in the layout (`ogImage` default) — reuse rather than invent a new asset, but confirm it visually represents the business (a plated dish or venue photo would be preferable to a wedding hero if one exists).
- `geo`, `sameAs`, `aggregateRating` intentionally omitted here — see dedicated sections below (all pending real data, not fabricated).
### 2. `FAQPage` on 4 pages — **PASS (structurally), Info-priority per current Google policy**
**Evidence:** `bodas-reales.astro`, `corporativo-business.astro`, `reuniones-familiares.astro`, `contacto.astro` each emit a `FAQPage` with 3 `Question`/`acceptedAnswer` pairs. Checked against rendered output — all 3 Q&A pairs on each page are also rendered as visible `` content in the same page, so there's no hidden/mismatched-content violation.
**Structural validation:**
- ✅ `@context: "https://schema.org"`
- ✅ `@type: "FAQPage"`, nested `Question`/`Answer` types correct
- ✅ Required properties present (`mainEntity`, `name`, `acceptedAnswer.text`)
- ✅ No placeholder text
- ✅ Content matches visible page content
**Policy note (per current Google guidance):** Google retired FAQ rich results for all sites (May 7, 2026), so this markup no longer produces a SERP rich result. Per current guidance this is **Info priority, not a defect** — keep it in place, since it still aids AI/LLM citation and entity/answer extraction (ChatGPT, Perplexity, Gemini, AI Overviews). No action required; do not remove it, and do not add more `FAQPage` blocks expecting SERP benefit — any future FAQ-style page should be evaluated as GEO/AI-visibility content, not a rich-result play.
One structural nit worth a look: these are genuinely marketing FAQ content (pricing/coverage questions the business anticipates), not a user-generated Q&A page, so `FAQPage` (not `QAPage`) is the correct type choice here — no change needed.
### 3. Missing: `BreadcrumbList` — **Opportunity (Medium)**
**Evidence:** No `BreadcrumbList` found on any page. Google supports breadcrumb rich results (still active, not deprecated), and the site has a clear, shallow hierarchy (`Home > Bodas Reales`, `Home > Corporativo & Business`, `Home > Reuniones Familiares`, `Home > Contacto`) that maps directly to nav (`SiteHeader.astro` `activeNav`).
**Recommendation** (example for `/bodas-reales`):
```json
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Inicio", "item": "https://www.mnqcatering.com/" },
{ "@type": "ListItem", "position": 2, "name": "Bodas Reales", "item": "https://www.mnqcatering.com/bodas-reales" }
]
}
```
Repeat per page with the matching second crumb (`Corporativo & Business` → `/corporativo-business`, `Reuniones Familiares` → `/reuniones-familiares`, `Contacto` → `/contacto`). Cheapest way to add this: compute it in `MainLayout.astro` from the existing `activeNav`/`title` props and pass it into the `structuredDataScripts` array alongside the FAQ block, rather than hand-writing it on each page.
### 4. Missing: `geo` (GeoCoordinates) — **Acknowledged, correctly deferred**
Confirmed absent from the `address` block. Per the known-state note, this is intentionally withheld pending a confirmed Google Business Profile pin rather than an oversight — correct call; do not fabricate lat/long. Once available:
```json
"geo": { "@type": "GeoCoordinates", "latitude": 36.xxxxx, "longitude": -4.xxxxx }
```
### 5. Missing: `sameAs` — **Acknowledged, correctly deferred**
No social profile URLs in the schema, matching the known state (no confirmed profiles yet). Do not add placeholder/guessed URLs. Once Instagram/Facebook/LinkedIn profiles are confirmed live and owned by the business:
```json
"sameAs": ["https://www.instagram.com/", "https://www.facebook.com/"]
```
### 6. Missing: `Review` / `AggregateRating` — **Acknowledged, correctly deferred**
No review schema present, matching known state. **Do not add this without real, verifiable reviews** — `AggregateRating`/`Review` markup requires genuine user-submitted reviews per Google's structured data policy; fabricating or estimating a rating is a policy violation that can trigger a manual action. Revisit once real reviews exist (Google Business Profile, testimonials with attribution, etc.).
### 7. Duplicate sitewide block per page — **Info, not a defect**
Every page repeats the full `CateringBusiness`/`CateringService` block rather than referencing a single `@id`-linked entity via `@graph`. This is common and acceptable practice for a small static-ish site (each page is self-contained for crawlers that don't execute JS or follow references), so not flagged as an issue — but if the block grows substantially (e.g., after adding `makesOffer`, `geo`, `sameAs`, `image`), consider consolidating into a `@graph` with `@id` references from per-page `WebPage`/`FAQPage` nodes to avoid repeating a large block on every request. Not urgent.
## Score: 62 / 100
**Deductions:**
- −25: Invalid `@type` (`CateringService` doesn't exist) on the one schema block present on every page of the site — this is the single highest-impact fix available, since it currently likely undermines rich-result/entity recognition for the whole business identity block.
- −5: Non-standard `serviceType` property riding on that invalid type.
- −5: `telephone` not in E.164 format (minor/cosmetic).
- −3: No `BreadcrumbList` despite a hierarchy that trivially supports it.
**Not deducted** (correctly deferred, not defects): missing `geo`, `sameAs`, `Review`/`AggregateRating` — all withheld pending real data, which is the right call rather than fabricating values.
## What already works well
- **JSON-LD only**, no legacy Microdata/RDFa to reconcile — clean baseline.
- **`https://` context**, absolute URLs throughout, ISO-correct address structure.
- **Server-rendered**, present in raw HTML (not client-injected) — fully crawlable without JS execution.
- **FAQ schema content matches visible page content exactly** — no hidden-text schema abuse anywhere.
- **Correct restraint** on `geo`, `sameAs`, and `AggregateRating` — nothing fabricated or guessed to fill gaps, which is exactly right per Google's structured-data policies.
- **No deprecated types used** (no `HowTo`, `SpecialAnnouncement`, `CourseInfo`, etc.).
- Address, `areaServed`, and `contactPoint` are properly structured and consistent across every page.