A release newsroom on a blog
A release newsroom is a blog whose posts are drops: a sneaker, a console, a vinyl pressing, a game. Each one has a release day that is not the day you publish the post, a price, a style code, the shops selling it and the regions it reaches, and readers come back to see what is out now and what is coming.
Forumulate does this on an ordinary Blog space. Nothing here is a separate product: the release facts are declared fields of a post kind, so the studio, the API, markdown front matter and folder sync all handle them the way they handle any other field.
The quickest start is the Dropline template (Studio → Templates → Dropline), which sets up everything on this page with sample drops.
1. Make posts into drops: the drop kind
A post's kind says which declared fields it carries. Two kinds ship for blogs:
Kind | What it is |
|---|---|
| A product release, with the release fields below. |
| A post that lists the releases in a window and keeps itself up to date. |
Give a post a kind in either of these ways:
One post at a time. In the studio, open the post's settings and choose Kind of post. Through the API, send
kindon the blog door:PATCH /api/orgs/{org}/spaces/{space}/blog/{id}with{"kind": "drop"}. A kind the space does not offer is refused with 422, and the error lists the kinds it does offer. Send{"kind": null}to take the kind off.Every post in the space. Make
dropthe default kind withPUT /api/orgs/{org}/spaces/{space}/shape/default-kindand{"kind": "drop"}, or apply the preset's fields to the space's own shape.
The release fields
Field | Type | What it holds |
|---|---|---|
| date ( | The day it drops, in the blog's time zone. This is separate from the publish date. |
| text | A window when the day isn't known yet ("Holiday 2026"), or alongside the day. |
|
| Where the release stands, as you set it. "Out now" is worked out for you (below). |
| money ( | The retail price. |
| text | The style code or SKU. |
| text | The colourway. |
| text | Sneakers, Apparel, Consoles and so on. The calendar and feeds can filter by it. |
| links ( | Where to buy: a name and an http(s) address for each shop, plus an optional note ("Raffle", "10am ET"). Up to 30. |
| several of | The regions it releases in. Leave it empty if you don't want to say. |
| text | What readers in other regions are told. |
retailers uses the links field type, a short list of named links. You can use it on any space type, for example for a product's stockists or a talk's slides and recording.
Setting the fields
All the usual ways of setting a field work:
Studio: the post's settings list the fields of its kind.
The blog door: send
fieldsonPOSTorPATCH …/blog. The values you send are merged with the stored ones, andnullclears a field. Values are checked against the post's kind exactly as/itemschecks them, so a wrong value is a 422 that names the field.{ "title": "Air Runner 84 “Varsity Red”", "kind": "drop", "fields": { "releaseDate": "2026-11-14", "releaseStatus": "upcoming", "retailPrice": { "min": 180, "currency": "USD" }, "sku": "AR8401-600", "colorway": "Varsity Red/Black-Sail", "releaseCategory": "Sneakers", "retailers": [ { "label": "Nike SNKRS", "url": "https://www.nike.com/launch", "note": "10am ET" }, { "label": "END. Launches", "url": "https://launches.endclothing.com", "note": "Raffle" } ], "regions": ["US", "EU", "UK"], "regionNote": "Not releasing where you are? END. ships to most countries." } }/items:PATCH …/items/{id}/fieldsandparamsonPOST …/items.Markdown front matter and folder sync (
forumulate publish): putkind: dropin a post's front matter, and write each field under its own key. The CLI reads the kind's fields and sends them.--- title: Air Runner 84 “Varsity Red” kind: drop releaseDate: 2026-11-14 releaseStatus: upcoming retailPrice: { min: 180, currency: USD } sku: AR8401-600 retailers: - { label: Nike SNKRS, url: "https://www.nike.com/launch" } regions: [US, EU, UK] ---
2. What readers see on a drop
The "Release details" block
A drop gets a Release details block. It shows the release day (with the window, if there is one), a status badge, the price in the reader's own number format, the style code, the colourway, the category, the regions, and a button for each retailer. It is rendered on the server, so it is part of the HTML a crawler reads and nothing moves around after the page loads.
"Out now", worked out for you
The badge is calculated each time the page is rendered, from the release day and the blog's own clock (theme.blog.release.timeZone, which defaults to UTC):
What you set | The badge says |
|---|---|
| Cancelled |
| Restocked |
| Out now |
| Delayed |
a release day in the future | Upcoming |
no day yet (only a window) | Date TBA |
You never have to flip a status at midnight. A drop dated the 14th reads "Out now" on the 14th, in the blog's time zone. The same badge appears on the drop's card in lists and feeds.
Regions
A drop that names regions lists them. Readers in another region see the drop's regionNote (or the theme's regions.note). The reader's country comes from our own edge, Cloudflare's CF-IPCountry header, and never from a geolocation service. Pages are cached at the edge for everyone, so a reader who isn't signed in gets the answer from a small same-origin request after the page loads (GET /api/public/geo, which is never cached). All three wordings sit in the same reserved space, so nothing shifts when the answer arrives. Signed-in readers have the answer rendered into the page directly. Set regions.personalise: false to show every reader the same note.
Structured data
Each drop adds a schema.org Product to the page's JSON-LD, next to its BlogPosting. It includes sku, color, category, releaseDate, and an Offer for each retailer with the price, currency, availability and availabilityStarts. To emit a SaleEvent on the release day instead, set structuredData: "event". A cancelled drop becomes an EventCancelled and a delayed one an EventRescheduled. Set "none" to emit neither. If a drop has no price, no offers are emitted, because an offer without a price is rejected by search engines.
3. The release calendar
The calendar lives at /<blog>/calendar, and in each language at /<blog>/<locale>/calendar. It has three views:
Month: six week rows (so moving between months never shifts what's below the grid). Each release in a day's cell links to its post. The day's number jumps to that day in the list under the grid, which is also what phones see.
Week: the seven days with every release.
List: the upcoming releases, soonest first, grouped by day.
Tag pills filter the calendar (?tag=sneakers). The address holds the state, so every view can be linked to: ?view=week&week=2026-11-09, ?month=2026-12.
The calendar is on once the blog has a published release with a date, or straight away if you set calendar.enabled: true. With enabled: false it is off, and the address goes to a post whose slug is calendar, if there is one. Turning the calendar on never hides a post that was already published at that address. While it is on, a Calendar link appears next to Archive on the blog's index, and the page is listed in the blog's sitemap.
The calendar is a blog hub page, so you can compose it like the archive: PUT …/blog/hubs/calendar/_/page.
4. Sorting and filtering by release
The blog index's public read (GET /api/public/orgs/{org}/spaces/{space}/blog) accepts these parameters:
Parameter | Meaning |
|---|---|
| Soonest release day first ( |
|
|
| A window of release days, inclusive. |
| What "today" is for |
| Comma-separated statuses, as you set them. |
| One category. Case doesn't matter. |
A value that can't be used gets a 422 that names the parameter. It never silently matches everything. The blog's own index shows Upcoming and Out now tabs next to Latest and Top once it has releases.
GET /api/public/orgs/{orgId}/spaces/{spaceId}/blog/releases returns the releases in a window, soonest first, with the same parameters plus tag. This is what the calendar reads.
On a front page
A spaceFeed that reads a blog can sort and filter by release:
{
"type": "spaceFeed",
"attrs": {
"sort": "release",
"filter": { "release": { "when": "upcoming", "category": "Sneakers" } },
"layout": "grid",
"fields": ["release", "cover", "title", "standfirst"]
}
}filter.release takes when (upcoming, out or any), range (below), from/to, status (a list) and category. The release card field is the drop's badge, day and price.
5. Roundups that assemble themselves
A roundup lists the releases whose day falls in a window, and it works out the window again each time the page is rendered. A "This week's drops" post you write once lists the right drops every week.
As a post: give it the
roundupkind and setroundupRange. The other fields areroundupFrom/roundupTo(forcustom),roundupTag,roundupCategoryandroundupLimit. The list is drawn under the post's body.As a band:
{"type": "spaceBand", "attrs": {"band": "releaseRoundup", "releaseRange": "next-month"}}. The band also takesreleaseFrom,releaseTo,releaseCategory,tags(only the first one is used),limitandheading.As a feed:
filter.release.range.
The ranges are this-week, next-week, this-month, next-month, next-7-days, next-30-days, past-7-days, upcoming (from today onward, with no end) and custom (between from and to). Weeks start on the calendar's weekStart day.
The calendar is also available as a band: {"band": "releaseCalendar", "releaseView": "week"}, which takes releaseView (month, week or list), releaseCategory, tags, limit and heading. It shows the grid with a link to the full calendar.
6. Every look is a key: theme.blog.release
Set these on the space theme (PATCH /api/orgs/{org}/spaces/{space}/theme) or on the site theme. The space theme wins over the site's, and the site's wins over the defaults. Each one is also in the studio's keys panel under Blog → release.
{
"blog": {
"release": {
"timeZone": "America/New_York",
"structuredData": "product",
"details": {
"position": "top",
"layout": "grid",
"frame": "rule",
"heading": "Release details",
"parts": ["date", "status", "price", "sku", "colorway", "regions", "retailers"],
"dateStyle": "long",
"priceStyle": "symbol",
"retailers": "buttons",
"accent": "#ff4d00"
},
"badge": {
"style": "tag",
"onCards": true,
"dateOnCards": true,
"colors": { "out": "#00b37e", "upcoming": "#ff4d00", "cancelled": "#b91c1c" },
"labels": { "out": "Live now" }
},
"calendar": {
"enabled": true,
"view": "month",
"weekStart": "monday",
"width": "wide",
"upcomingLimit": 12,
"covers": true,
"prices": true,
"tagFilter": true,
"inNav": true,
"showCancelled": true,
"todayColor": "#ff4d00"
},
"regions": { "style": "chips", "personalise": true },
"roundup": { "layout": "grid", "groupBy": "day", "covers": true }
}
}
}Key | Default | What it does |
|---|---|---|
|
| The blog's clock, used to decide "today" for Out now, the calendar and roundups. |
|
|
|
| on / | Whether to show the block, and where: |
|
|
|
| every part the post has | Which parts to show, and in what order. |
|
|
|
|
|
|
| green / brand / amber / red | A colour and a word for each state. |
| see above | Whether it's on, its view, week start, width, list length, covers, prices, tag filter, nav link, cancelled releases and today's colour. |
|
| How regions are shown, the note when a post has none of its own, and whether to tell readers if it's available where they are. |
|
|
|
Unknown keys get a 422 that names them, as with every theme object. The words the reader shows (the badge states, "Release details", the calendar's buttons) come in every language the reader supports, and you can replace them with the labels keys.
In the API
Every operation below is also reachable from the other surfaces without a dedicated command. forumulate api <Operation> calls it from the CLI, the JavaScript SDK has a typed method for each one (the last column names it; the Python and Go clients are generated from the same spec), and an agent reaches the organization-scoped ones through the MCP call_api tool. Where a CLI command or MCP tool exists too, the last column names it. Operation names link to the API reference.
Public Blog Releases
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| List releases in a space |
|
Public Geo
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| Get the geo |
|