Skip to main content
This domain covers the storefront content layer: blog containers and their articles, static pages, custom domain management (with Caddy-backed provisioning and Plausible analytics integration), theme configuration for storefronts, and the media library (both direct file upload and S3 presigned / multipart upload). Public read endpoints for blogs and pages require no authentication; all write operations and the full media/upload surface require a valid bearer token and company header. Base URL https://api.genuka.com · Auth Authorization: Bearer <token> + X-Company: <companyId> · Getting Started

Endpoints at a glance


Blogs

Blogs are content containers that group articles. Each blog belongs to a company and has a URL-safe handle that is unique within that company. A blog can have an optional cover image (stored in the Spatie media library). The articles relationship can be sideloaded.

GET /2023-11/admin/blogs

List all blogs for the authenticated company. Supports search and sorting. Query parameters
Success response (200 OK):
Error responses
  • 401 — Missing or invalid bearer token.
  • 403 — Token does not have access to the specified company.

POST /2023-11/admin/blogs

Create a new blog for the authenticated company. Request body
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 403 — Unauthorized action.

GET /2023-11/admin/blogs/handle/{blog}

Retrieve a single blog by its handle slug (admin route). Path parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 404 — No blog found with that handle.

GET /2023-11/admin/blogs/{blog}

Retrieve a single blog by its ULID (admin route). Path parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 404 — Blog not found.

PUT /2023-11/admin/blogs/{blog}

Update a blog’s fields. Only supplied fields are changed. Path parameters Request body
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 404 — Blog not found.

DELETE /2023-11/admin/blogs/{blog}

Permanently delete a blog. Path parameters
Success response (200 OK): empty body (no content). Error responses
  • 401 — Unauthenticated.
  • 404 — Blog not found.

GET /2023-11/blogs

List all blogs for a storefront (public, no auth required). Accepts the same filter/sort/pagination query parameters as the admin list endpoint. Query parameters
Success response (200 OK):
Error responses
  • 403 — Company header missing or invalid.

GET /2023-11/blogs/count

Return the total number of blogs for the company. No pagination.
Success response (200 OK):

GET /2023-11/blogs/handle/{blog}

Retrieve a single blog by handle (public). Path parameters
Success response (200 OK):
Error responses
  • 404 — Blog not found.

GET /2023-11/blogs/{blog}

Retrieve a single blog by ULID (public). Path parameters
Success response (200 OK):
Error responses
  • 404 — Blog not found.

Pages

Static CMS pages (e.g. Terms & Conditions, About Us). Each page belongs to a company, has a unique handle, a rich text content field, optional metadata, and an optional cover image. The public endpoints require only X-Company.

GET /2023-11/admin/pages

List all pages for the authenticated company. Query parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 403 — Unauthorized.

POST /2023-11/admin/pages

Create a new CMS page. Request body
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 422 — Validation failed (e.g. duplicate handle within company).

GET /2023-11/admin/pages/handle/{page}

Retrieve a single page by its handle (admin). Path parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 404 — Page not found.

GET /2023-11/admin/pages/{page}

Retrieve a single page by ULID (admin). Path parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 404 — Page not found.

PUT /2023-11/admin/pages/{page}

Update a page. Only supplied fields are changed. Path parameters Request body
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 404 — Page not found.

DELETE /2023-11/admin/pages/{page}

Permanently delete a page. Path parameters
Success response (204 No Content): empty body. Error responses
  • 401 — Unauthenticated.
  • 404 — Page not found.

GET /2023-11/pages

List all pages (public storefront). Same filter/sort/pagination as the admin list.
Success response (200 OK):

GET /2023-11/pages/count

Return the total number of pages for the company.
Success response (200 OK):

GET /2023-11/pages/handle/{page}

Retrieve a page by handle (public). Path parameters
Success response (200 OK):
Error responses
  • 404 — Page not found for that handle.

GET /2023-11/pages/{page}

Retrieve a page by ULID (public). Path parameters
Success response (200 OK):
Error responses
  • 404 — Page not found.

Domains

Custom domain management. When a domain is created or updated the API provisions it on the Caddy reverse proxy and registers it with Plausible for analytics tracking. Custom (non-.genuka.com) domains must have their DNS A record pointing to the server’s IP before the API will accept them.

GET /2023-11/admin/domains

List all domains for the authenticated company. Query parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.

POST /2023-11/admin/domains

Register a new custom domain. For non-.genuka.com domains the DNS A record must point to the Caddy server IP before the request is made. Request body
Success response (200 OK):
Error responses
  • 400 — Domain is not allowed (excluded list or DNS does not point to server).
  • 400 — Company already has the maximum number of .genuka.com subdomains.
  • 422domain is required or already taken.
  • 500 — Caddy provisioning failed.

GET /2023-11/admin/domains/{domain}

Retrieve a single domain by ULID. Path parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 404 — Domain not found.

PUT /2023-11/admin/domains/{domain}

Update a domain’s hostname or main-domain flag. If the hostname changes, the old Caddy site is removed and a new one is provisioned. Path parameters Request body
Success response (200 OK):
Error responses
  • 400 — Domain not allowed or DNS check failed.
  • 422 — Validation errors.
  • 500 — Caddy provisioning failed.

DELETE /2023-11/admin/domains/{domain}

Remove a domain and de-provision it from the Caddy reverse proxy. If this was the main domain, the first remaining domain is automatically promoted. Path parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 404 — Domain not found.
  • 500 — Caddy de-provisioning failed.

Domain Analytics

Analytics are powered by a Plausible instance. All analytics endpoints accept a date range via query parameters; dates default to the last 30 days when omitted.

GET /2023-11/admin/domains/{domain}/analytics

Aggregate traffic metrics (visitors, pageviews, bounce rate, visit duration) for a domain. Path parameters Query parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 404 — Domain not found.
  • 500 — Plausible API error.

GET /2023-11/admin/domains/{domain}/analytics/timeseries

Daily visitor and visit counts for a date range. Path parameters Query parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 500 — Plausible API error.

GET /2023-11/admin/domains/{domain}/analytics/top-countries

Top 10 visitor countries for a domain in a date range. Path parameters Query parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 500 — Plausible API error.

GET /2023-11/admin/domains/{domain}/analytics/top-devices

Top 10 device types (Desktop, Mobile, Tablet) for a domain in a date range. Path parameters Query parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 500 — Plausible API error.

GET /2023-11/admin/domains/{domain}/analytics/top-pages

Top 10 most visited pages on a domain in a date range. Path parameters Query parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 500 — Plausible API error.

GET /2023-11/admin/domains/{domain}/analytics/top-sources

Top 10 referral/traffic sources for a domain in a date range. Path parameters Query parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 500 — Plausible API error.

Theme Configurations

Theme configurations link a storefront template to a company and hold a JSON configuration blob (colors, fonts, layout settings, etc.). Setting is_active to true promotes that theme and re-provisions all company domains in Caddy. Only one theme can be active at a time; activating a new one automatically deactivates all others.

GET /2023-11/admin/theme-configurations

List all theme configurations for the authenticated company. Query parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.

GET /2023-11/admin/theme-configurations/{themeConfiguration}

Retrieve a single theme configuration by ULID. Eager-loads template and company. Path parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 404 — Theme configuration not found.

PUT /2023-11/admin/theme-configurations/{themeConfiguration}

Update a theme configuration. Send either configuration to update the theme settings JSON, or is_active to switch the active theme. Mixing both in a single request is not supported — the handler checks configuration first. Path parameters Request body
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 404 — Theme configuration not found.
  • 500 — Caddy re-provisioning failed when activating a theme.

Media Library

Uploaded files are stored via Spatie Media Library and served from S3. Each media record belongs to a model (product, blog, page, company, etc.) and has image conversion variants (micro, thumb, large, webp). Access is permission-gated: users with manage_media_files see all company media; others see only the media associated with models they have permission to view.

GET /2023-11/admin/medias

List media files for the authenticated company. Query parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 404 — Company not found.

POST /2023-11/admin/medias

Upload one or more media files to the company library. Supports two modes:
  1. Traditional multipart upload — send files in a files[] form-data field.
  2. S3 presigned registration — after uploading directly to S3 via POST /upload/presign, register the uploaded keys by sending a medias JSON array.
Request body — multipart file upload (multipart/form-data) Request body — S3 key registration (application/json)
Success response (200 OK): array of MediaResource objects (same shape as list items).
Error responses
  • 400 — Error registering S3 key or uploading file.
  • 401 — Unauthenticated.
  • 404 — Company not found or files field missing.
  • 500 — Internal upload error.

GET /2023-11/admin/medias/stats

Return aggregate usage statistics for the company media library: total file count, total storage in bytes, a breakdown by model type, and a breakdown by S3 folder (top 50 folders by file count).
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 404 — Company not found.

DELETE /2023-11/admin/medias/{media}

Permanently delete a media record (and its associated S3 file / conversions). Path parameters
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 404 — Media record not found.

S3 Upload

These endpoints facilitate direct client-to-S3 uploads, bypassing the API server for file transfer. The recommended flow for large files is: POST /upload/presign → PUT to the presigned URL → POST /admin/medias (S3 key registration). For files over 5 MB, use the multipart flow: POST /upload/multipart/create → PUT each part → POST /upload/multipart/completePOST /admin/medias. S3 keys are scoped to company/<companyId>/<model>/<collection>/ to enforce tenant isolation. The DELETE /upload/by-key endpoint enforces the same prefix restriction.

POST /2023-11/upload/presign

Generate a presigned S3 PUT URL for single-file direct upload. The client uploads the file directly to S3 using the returned URL, then registers the object with POST /admin/medias. Request body
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 422 — Missing required field (content_type, ext, model, or collection).

POST /2023-11/upload/multipart/create

Initiate an S3 multipart upload and receive presigned PUT URLs for each part. Use for files larger than 5 MB. Parts must be at least 5 MB each (except the last). Request body
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 422 — Validation failed (e.g. part_count out of range).

POST /2023-11/upload/multipart/complete

Complete a multipart upload after all parts have been PUT to S3. The client must supply the ETag header value returned by S3 for each part. Request body
Success response (200 OK): the AWS S3 CompleteMultipartUpload result object, typically including the final object Location, Bucket, Key, and ETag.
Error responses
  • 401 — Unauthenticated.
  • 422 — Missing or invalid key, upload_id, or parts fields.

POST /2023-11/upload/multipart/abort

Abort an in-progress multipart upload, freeing any partially uploaded parts from S3. Request body
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 422 — Missing key or upload_id.

DELETE /2023-11/upload/by-key

Delete an S3 object directly by its key. Only keys under the authenticated company’s prefix (company/<companyId>/) are permitted. Request body
Success response (200 OK):
Error responses
  • 401 — Unauthenticated.
  • 403 — Key does not start with the authenticated company’s prefix ({ "message": "Unauthorized key prefix" }).
  • 422key field missing.