Skip to content

Write a blog post

POST
/storefront/posts
curl --request POST \
--url https://example.com/storefront/posts \
--header 'Content-Type: application/json' \
--cookie __Host-bottle.session_token=<__Host-bottle.session_token> \
--data '{ "title": "example", "slug": "refills-and-swaps", "excerpt": "", "bodyHtml": null, "coverKey": null, "coverAlt": "", "authorName": "", "publishedOn": null, "tags": [], "seoTitle": "", "seoDescription": "", "status": "draft" }'

A new post, a draft unless status says published. Without a slug, one is made from the title. A published post shows from publishedOn, the day it is first published unless given one, so it can be written ahead. The body is rich text: paragraphs, two sizes of heading, bold, italic, lists, quotes, links (https, email, or a path on the shop such as /delivery), pictures and product blocks. Anything else is removed, and it is cleaned again whenever it is read. A picture is <img src="/api/public/images/{key}/large" alt="…"> with a key from POST /storefront/assets and words saying what it shows; one without words, or from anywhere else, is dropped. A product block is <bottle-products data-ids="{id},{id}"></bottle-products> with up to four of the business’s own product ids; the shop draws each as a card with its live name, picture and price, and leaves out any not on sale.

Media typeapplication/json
object
title
required
string
>= 1 characters <= 120 characters
slug

Lower-case letters, digits and single dashes, as in refills-and-swaps.

string
>= 1 characters <= 80 characters /^[a-z0-9]+(?:-[a-z0-9]+)*$/

Example

refills-and-swaps
excerpt
string
"" <= 300 characters
bodyHtml
string | null
<= 200000 characters
coverKey
string | null
<= 300 characters
coverAlt
string
"" <= 300 characters
authorName
string
"" <= 80 characters
publishedOn
string | null format: date
tags

Up to five short tags, such as Barbecues or Safety.

Array<string>
default: <= 5 items
seoTitle
string
"" <= 70 characters
seoDescription
string
"" <= 300 characters
status

Draft: only the business sees it, in the preview. published: on the shop.

string
default: draft
Allowed values: draft published

The post

Media typeapplication/json
object
id
required

UUID v7

string format: uuid
slug
required
string
title
required
string
excerpt
required
string
bodyHtml
required
string | null
coverKey
required
string | null
coverAlt
required
string
cover
required
object
alt
required
string | null
width
required
integer
height
required
integer
thumb
required
string
card
required
string
large
required
string
authorName
required
string
publishedOn
required
string | null format: date
tags
required
Array<string>
seoTitle
required
string
seoDescription
required
string
status
required

Draft: only the business sees it, in the preview. published: on the shop.

string
Allowed values: draft published
createdAt
required
string format: date-time
updatedAt
required
string format: date-time
oldSlugs
required
Array<string>

Example

{
"id": "019205d1-6e7a-7c3b-9f6e-3a2b1c0d9e8f",
"status": "draft"
}

Not signed in

Media typeapplication/json
object
error
required
string
message
required
string

Example

{
"error": "not_found",
"message": "Customer not found"
}

The plan does not include a shop

Media typeapplication/json
object
error
required
string
message
required
string

Example

{
"error": "not_found",
"message": "Customer not found"
}

Not a member, or without the permission this needs

Media typeapplication/json
object
error
required
string
message
required
string

Example

{
"error": "not_found",
"message": "Customer not found"
}

Another post has that address

Media typeapplication/json
object
error
required
string
message
required
string

Example

{
"error": "not_found",
"message": "Customer not found"
}

Validation failed

Media typeapplication/json
object
error
required
string
Allowed values: validation
message
required
string
issues
required
Array<object>
object
path
required
string
message
required
string

Example

{
"error": "validation",
"issues": [
{
"path": "email",
"message": "Invalid email"
}
]
}