{"openapi":"3.1.0","info":{"title":"Publino Delivery API","version":"1.0.0","description":"The **Publino Delivery API** is a read-only API for fetching your published articles.\n\n## Getting started\nBase URL: `https://api.publino.de/v1`. You need a **site id** and a **key** — both are\ncreated on the **Sites** page in your Publino dashboard.\n\n## Authentication — two modes\n- **Secret key** (`pub_del_…`) — send as `Authorization: Bearer <key>`. Server-side only;\n  never expose it in a browser. Use this from your backend or the PHP SDK.\n- **Publishable key** (`pub_pk_…`) — send as the `?pk=<key>` query parameter. Browser-safe,\n  but you must allowlist the calling **Origin(s)** on the Sites page. Requests from a\n  non-allowlisted origin receive `403`. Use this from the embed script or client-side code.\n\nEither key is scoped to a **single site**: the `siteId` in the path must match the key's site.\n\n## Locales\nPass `?locale=` to choose a translation. If omitted, the site's first supported locale is used.\nAn unsupported locale returns `400`.\n\n## Caching\nResponses send `Cache-Control: public, max-age=60`. Single-article responses also send a\n**weak** `ETag` (`W/\"…\"`) — send it back as `If-None-Match` to get a `304 Not Modified`.\n\n## Rate limiting\nExceeding the limit returns `429` with a `Retry-After` header (seconds).\n\n## Embed (Web Components)\nFor a no-backend integration, drop the `embed.js` script into your page with a publishable\nkey and `site`/`article-id` (single) or list attributes — articles render client-side.\nPass the public id (or a `{slug}-{id}` URL-friendly string) as the `article-id` value.\nThe ready-to-paste snippet is shown on the Sites page.\n\n## PHP SDK\n`publino/sdk` is a zero-dependency cURL client for the secret-key flow. Point it at the base\nURL `https://api.publino.de`.\n\n## Webhooks (outbound)\nPublino can POST `article.published`, `article.unpublished`, and `article.updated` events to\nan endpoint you configure per site in the dashboard. These are **sent by** Publino and are not\npart of this read API."},"servers":[{"url":"https://api.publino.de/v1","description":"Primary — API-only subdomain"},{"url":"https://app.publino.de/v1","description":"App host"}],"security":[{"bearerAuth":[]},{"publishableKey":[]}],"paths":{"/sites/{siteId}/articles":{"get":{"operationId":"listArticles","summary":"List published articles","description":"Returns all published articles for the site in the requested locale.","parameters":[{"name":"siteId","in":"path","required":true,"description":"The site id (matches your key's site).","schema":{"type":"string"}},{"name":"locale","in":"query","required":false,"description":"Translation locale (e.g. `en`). Defaults to the site's first supported locale.","schema":{"type":"string"}},{"name":"category","in":"query","required":false,"description":"Filter by category id.","schema":{"type":"string"}},{"name":"tag","in":"query","required":false,"description":"Filter by tag id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Published articles for the locale.","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"public, max-age=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArticleList"}}}},"400":{"description":"Missing or unsupported locale.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Origin not allowed (publishable key only).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{siteId}/categories":{"get":{"operationId":"listCategories","summary":"List a site's categories","description":"Returns all categories for the site in the requested locale.","parameters":[{"name":"siteId","in":"path","required":true,"description":"The site id (matches your key's site).","schema":{"type":"string"}},{"name":"locale","in":"query","required":false,"description":"Translation locale (e.g. `en`). Defaults to the site's first supported locale.","schema":{"type":"string"}}],"responses":{"200":{"description":"Categories for the locale.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CategoryList"}}}},"401":{"description":"Missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Origin not allowed (publishable key only).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{siteId}/tags":{"get":{"operationId":"listTags","summary":"List a site's tags","description":"Returns all tags for the site in the requested locale.","parameters":[{"name":"siteId","in":"path","required":true,"description":"The site id (matches your key's site).","schema":{"type":"string"}},{"name":"locale","in":"query","required":false,"description":"Translation locale (e.g. `en`). Defaults to the site's first supported locale.","schema":{"type":"string"}}],"responses":{"200":{"description":"Tags for the locale.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagList"}}}},"401":{"description":"Missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Origin not allowed (publishable key only).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sites/{siteId}/articles/{ref}":{"get":{"operationId":"getArticle","summary":"Get a published article","description":"Returns one published article (with rendered HTML and source Markdown).","parameters":[{"name":"siteId","in":"path","required":true,"description":"The site id (matches your key's site).","schema":{"type":"string"}},{"name":"ref","in":"path","required":true,"description":"The article's stable public id. The slug in the URL is decorative — the value after the last `-` is used (e.g. `my-post-ab12cd34`).","schema":{"type":"string"}},{"name":"locale","in":"query","required":false,"description":"Translation locale (e.g. `en`). Defaults to the site's first supported locale.","schema":{"type":"string"}}],"responses":{"200":{"description":"The published article.","headers":{"Cache-Control":{"schema":{"type":"string"},"description":"public, max-age=60"},"ETag":{"schema":{"type":"string"},"description":"Weak validator, e.g. W/\"abc123\"."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Article"}}}},"304":{"description":"Not modified — the If-None-Match value matched the weak ETag."},"400":{"description":"Missing or unsupported locale.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Origin not allowed (publishable key only).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No published article with that id in this locale.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Secret delivery key (`pub_del_…`) via `Authorization: Bearer`. Server-side only."},"publishableKey":{"type":"apiKey","in":"query","name":"pk","description":"Publishable key (`pub_pk_…`) via `?pk=`. Browser-safe; requires an allowlisted Origin."}},"schemas":{"Category":{"type":"object","required":["id","name","slug"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}}},"CategoryList":{"type":"object","required":["site","locale","categories"],"properties":{"site":{"type":"string"},"locale":{"type":"string"},"categories":{"type":"array","items":{"$ref":"#/components/schemas/Category"}}}},"Tag":{"type":"object","required":["id","name","slug"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}}},"TagList":{"type":"object","required":["site","locale","tags"],"properties":{"site":{"type":"string"},"locale":{"type":"string"},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"}}}},"ArticleSummary":{"type":"object","required":["id","title","slug","locale","publishedAt"],"properties":{"id":{"type":"string"},"title":{"type":"string"},"slug":{"type":"string"},"locale":{"type":"string"},"excerpt":{"type":["string","null"]},"metaDescription":{"type":["string","null"]},"canonicalUrl":{"type":["string","null"]},"publishedAt":{"type":"string","format":"date-time"},"category":{"oneOf":[{"$ref":"#/components/schemas/Category"},{"type":"null"}]},"tags":{"type":"array","items":{"$ref":"#/components/schemas/Tag"}},"readingMinutes":{"type":"integer","description":"Estimated reading time in minutes (~150 wpm), minimum 1."}}},"Article":{"allOf":[{"$ref":"#/components/schemas/ArticleSummary"},{"type":"object","required":["markdown","html"],"properties":{"markdown":{"type":"string","description":"Source Markdown (placeholders substituted)."},"html":{"type":"string","description":"Server-rendered, sanitized HTML."},"jsonLd":{"type":["object","null"],"additionalProperties":true,"description":"schema.org JSON-LD for this article (ready to embed in a <script type=\"application/ld+json\">), or null if no schema type is set."}}}]},"ArticleList":{"type":"object","required":["site","locale","articles"],"properties":{"site":{"type":"string","description":"The site id."},"locale":{"type":"string"},"articles":{"type":"array","items":{"$ref":"#/components/schemas/ArticleSummary"}}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Machine-readable error, e.g. \"unauthorized\"."}}}}}}