# Delivery API

Suprafața publică, fără autentificare, care servește conținutul tău
publicat. Totul e indexat după cheia publică a site-ului tău (`pk_…`) și
funcționează doar pentru site-uri [verificate](/ro/docs/verification). `GET`
și `HEAD` sunt suportate peste tot.

Originea de bază: `https://api.dailysmith.com`.

## Pagini randate

```
GET /v1/render/{publicKey}/{path}
```

`{path}` e calea **așa cum apare pe site-ul tău** — ex. `/blog` pentru
index, `/blog/my-post-slug` pentru un articol. baas rezolvă ce blog deține
calea după calea lui de bază, deci proxy, SDK și prerender folosesc toate
aceeași schemă de URL-uri.

| Parametru query | Sens |
|---|---|
| `format=fragment` | Fragment JSON pentru SDK, în loc de o pagină HTML completă |
| `page=N` | Paginare index (10 articole per pagină) |

O pagină randată e servită întotdeauna în [limba](/ro/docs/content-pipeline)
blogului ei — aici nu există parametru `locale`. Un blog are o singură limbă,
așa că singurul lucru pe care l-ar putea produce altă valoare e o pagină goală
care declară o limbă în care nu are nimic, și asta nu merită dată unui crawler.
Dacă publici într-o a doua limbă, fă un al doilea blog. Listarea structurată de
mai jos acceptă `locale`, pentru că acolo un răspuns gol explicit la „aveți
ediție în germană?" chiar folosește.

**Paginile complete** sunt documente HTML complete, cu titlu, meta
descriere, taguri Open Graph și Twitter card (inclusiv `og:image`, când
articolul are o [imagine reprezentativă](/ro/docs/rich-content)), URL
canonical, JSON-LD, un stil implicit lizibil și [paleta ta de
temă](/ro/docs/theming) inline.

**Fragmentele** (`format=fragment`) returnează:

```json
{
  "html": "<article class=\"baas-post\">…</article>",
  "kind": "post",
  "title": "Titlul articolului",
  "description": "Meta descriere",
  "canonical": "https://example.com/blog/my-post-slug",
  "json_ld": "{…}",
  "locale": "en",
  "og_locale": "en_US",
  "css": "/* paleta de temă a blogului, doar când una e setată */",
  "css_href": "/v1/sdk/md2.css",
  "hero_image_url": "https://api.dailysmith.com/v1/media/01a01789-959f-78f7-a5d0-78fc208903f6",
  "hero_image_alt": "Ilustrație abstractă pentru Titlul articolului",
  "hero_image_width": 1376,
  "hero_image_height": 768
}
```

`locale` e [limba blogului](/ro/docs/content-pipeline) ca tag simplu — pune-o pe
elementul în care inserezi fragmentul, altfel articolul e anunțat de cititorul
de ecran în limba pe care o declară pagina ta. `og_locale` e aceeași limbă în
forma `limbă_TERITORIU` pe care o cere Open Graph; setează-l ca
`<meta property="og:locale">`, altfel cardul de distribuire e presupus
`en_US`. SDK-ul nostru pune întotdeauna `lang` pe fragmentul pe care îl
inserează, iar `og:locale` îl setează și pe el, dacă nu ai renunțat la
gestionarea head-ului.

Cele patru câmpuri `hero_image_*` sunt **omise complet** pentru un articol
fără [imagine reprezentativă](/ro/docs/rich-content), adică fiecare articol
scris înainte să le activezi. Imaginea e *și* deja în interiorul `html`-ului,
ca `<img class="baas-hero">` — un singur motor de randare servește fiecare
mod de livrare, deci o obții gratis doar prin inserarea fragmentului.
Câmpurile separate există ca să poți construi un card social sau o miniatură
de listare fără să parsezi marcajul; propriul nostru SDK le citește ca să
seteze `og:image` și le curăță din nou când navighează către un articol care
nu are una. Imaginea e servită de la `https://api.dailysmith.com/v1/media/{id}` —
permanentă, imuabilă și publică după id, deci nu are nevoie de nicio cheie
și poate fi pusă în cache pentru totdeauna.

`css` și `css_href` sunt **omise** amândouă când nu se aplică, nu trimise
goale: `css` poartă doar [paleta de temă](/ro/docs/theming) a blogului —
niciodată foaia de stil a componentelor — deci lipsește când nu există o
temă activă; `css_href` indică spre foaia de stil comună, cacheable, de mai
jos, și e prezent doar când conținutul chiar folosește componente MD2.
Paginile complete le inline-uiesc pe amândouă, în loc să le lege. Pentru
index (`kind: "index"`), `description` și `json_ld` sunt mereu șiruri
goale, nu omise.

## Listări structurate

```
GET /v1/delivery/{publicKey}/blogs
GET /v1/delivery/{publicKey}/posts?base_path=/blog&locale=en&page=1
```

`blogs` listează blogurile site-ului (nume, cale de bază, locale implicit).
`posts` listează articolele publicate pentru un blog — slug, titlu,
rezumat, cale pe site, dată de publicare, minute de citire — paginat
(răspunsul poartă și `page` și `total_pages`) — utile ca să-ți construiești
propriul UI de index sau să alimentezi alte sisteme.

## Fluxuri

```
GET /v1/delivery/{publicKey}/sitemap.xml
GET /v1/delivery/{publicKey}/rss.xml
```

Sitemap-ul acoperă indexurile de blog și fiecare articol publicat, cu date
de ultima modificare; RSS-ul poartă cele mai recente 50 de articole.
Referențiază sitemap-ul din `robots.txt`-ul tău:

```
Sitemap: https://api.dailysmith.com/v1/delivery/pk_YOUR_KEY/sitemap.xml
```

## Scriptul SDK

```
GET /v1/sdk/blog.js
GET /v1/sdk/md2.css
```

[JS SDK-ul](/ro/docs/install-sdk) și foaia de stil pentru conținut bogat MD2
spre care indică `css_href`-ul unui fragment — ambele servite cu headere de
cache pe termen lung.

## Erori

Fiecare eroare e `problem+json` ([RFC 9457](https://www.rfc-editor.org/rfc/rfc9457)):

```json
{ "type": "about:blank", "title": "Not found", "status": 404, "detail": "…" }
```

| Unde | Status | `detail` | Sens |
|---|---|---|---|
| Orice endpoint | 404 | `unknown or unverified site key` | Cheia nu există, sau site-ul nu a trecut de [verificare](/ro/docs/verification) — indistincte intenționat, ca o cheie să nu poată fi folosită ca să sondezi ce site-uri există. |
| `/v1/render/{publicKey}/{path}` | 404 | `no blog is mounted at this path` | Niciun blog nu are calea de bază potrivită cu `{path}`. |
| `/v1/render/{publicKey}/{path}` | 404 | `no such post` | Calea a rezolvat un blog, dar slug-ul nu. |
| `/v1/delivery/{publicKey}/posts` | 404 | `no blog at that base path` | Parametrul query `base_path` nu se potrivește cu niciun blog. |
| `/v1/sdk/md2.css` | 503 | `stylesheet temporarily unavailable` | Sidecar-ul de randare e inaccesibil — reîncearcă. |

Nimic pe această suprafață nu returnează vreodată 402 sau 422 — erorile de
cotă și de validare se aplică doar API-ului autentificat de conținut.

## Semantica de cache

- Fiecare răspuns poartă un **ETag puternic** (un hash al conținutului) și
  respectă `If-None-Match` cu `304 Not Modified`.
- `Cache-Control: public, max-age=60, s-maxage=300, stale-while-revalidate=600`
  — prietenos cu CDN-urile și cu cache-ul tău de edge; schimbările de
  conținut (un articol nou, o temă editată) schimbă ETag-ul.
- CORS e complet deschis (`Access-Control-Allow-Origin: *`): conținutul e
  public prin definiție, ceea ce îi permite SDK-ului să-l preia de pe
  domeniul tău.

:::callout{severity=warning title="Cheie publică, nu cheie secretă"}
Cheia `pk_…` îți identifică site-ul și e sigur s-o trimiți în HTML — e
singura cheie pe care o acceptă acest API. Site-ul tău are și o cheie
secretă (`sk_…`), pe care niciun endpoint nu o acceptă deocamdată: API-ul
panoului se autentifică prin sesiunea ta, nu printr-o cheie. Tratează
totuși `sk_…` ca pe o credențială — ține-o departe de codul client-side și
rotește-o din pagina **Chei API** a site-ului dacă ajunge să fie expusă.
:::
