curl --request POST \
--url https://app.orgo.space/api/v1/website_articles \
--header 'Api-Token: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"slug": "annual-general-meeting-2026",
"title": "Volunteer Coordinator",
"excerpt": "string",
"coverImage": "https://cdn.orgo.space/events/12/cover.jpg",
"category": "GENERAL",
"blocks": [
"string"
],
"status": "ACTIVE",
"publishedAt": "2026-01-15T10:30:00+00:00",
"seoTitle": "Annual General Meeting 2026",
"seoDescription": "Quarterly chapter meeting open to all members."
}
'import requests
url = "https://app.orgo.space/api/v1/website_articles"
payload = {
"slug": "annual-general-meeting-2026",
"title": "Volunteer Coordinator",
"excerpt": "string",
"coverImage": "https://cdn.orgo.space/events/12/cover.jpg",
"category": "GENERAL",
"blocks": ["string"],
"status": "ACTIVE",
"publishedAt": "2026-01-15T10:30:00+00:00",
"seoTitle": "Annual General Meeting 2026",
"seoDescription": "Quarterly chapter meeting open to all members."
}
headers = {
"Api-Token": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Api-Token': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
slug: 'annual-general-meeting-2026',
title: 'Volunteer Coordinator',
excerpt: 'string',
coverImage: 'https://cdn.orgo.space/events/12/cover.jpg',
category: 'GENERAL',
blocks: ['string'],
status: 'ACTIVE',
publishedAt: '2026-01-15T10:30:00+00:00',
seoTitle: 'Annual General Meeting 2026',
seoDescription: 'Quarterly chapter meeting open to all members.'
})
};
fetch('https://app.orgo.space/api/v1/website_articles', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.orgo.space/api/v1/website_articles",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'slug' => 'annual-general-meeting-2026',
'title' => 'Volunteer Coordinator',
'excerpt' => 'string',
'coverImage' => 'https://cdn.orgo.space/events/12/cover.jpg',
'category' => 'GENERAL',
'blocks' => [
'string'
],
'status' => 'ACTIVE',
'publishedAt' => '2026-01-15T10:30:00+00:00',
'seoTitle' => 'Annual General Meeting 2026',
'seoDescription' => 'Quarterly chapter meeting open to all members.'
]),
CURLOPT_HTTPHEADER => [
"Api-Token: <api-key>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.orgo.space/api/v1/website_articles"
payload := strings.NewReader("{\n \"slug\": \"annual-general-meeting-2026\",\n \"title\": \"Volunteer Coordinator\",\n \"excerpt\": \"string\",\n \"coverImage\": \"https://cdn.orgo.space/events/12/cover.jpg\",\n \"category\": \"GENERAL\",\n \"blocks\": [\n \"string\"\n ],\n \"status\": \"ACTIVE\",\n \"publishedAt\": \"2026-01-15T10:30:00+00:00\",\n \"seoTitle\": \"Annual General Meeting 2026\",\n \"seoDescription\": \"Quarterly chapter meeting open to all members.\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Api-Token", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.orgo.space/api/v1/website_articles")
.header("Api-Token", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"slug\": \"annual-general-meeting-2026\",\n \"title\": \"Volunteer Coordinator\",\n \"excerpt\": \"string\",\n \"coverImage\": \"https://cdn.orgo.space/events/12/cover.jpg\",\n \"category\": \"GENERAL\",\n \"blocks\": [\n \"string\"\n ],\n \"status\": \"ACTIVE\",\n \"publishedAt\": \"2026-01-15T10:30:00+00:00\",\n \"seoTitle\": \"Annual General Meeting 2026\",\n \"seoDescription\": \"Quarterly chapter meeting open to all members.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.orgo.space/api/v1/website_articles")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Api-Token"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"slug\": \"annual-general-meeting-2026\",\n \"title\": \"Volunteer Coordinator\",\n \"excerpt\": \"string\",\n \"coverImage\": \"https://cdn.orgo.space/events/12/cover.jpg\",\n \"category\": \"GENERAL\",\n \"blocks\": [\n \"string\"\n ],\n \"status\": \"ACTIVE\",\n \"publishedAt\": \"2026-01-15T10:30:00+00:00\",\n \"seoTitle\": \"Annual General Meeting 2026\",\n \"seoDescription\": \"Quarterly chapter meeting open to all members.\"\n}"
response = http.request(request)
puts response.read_body{
"uuid": "01938d8e-9c4f-7c2a-b8e1-3f7a9b8c4f12",
"slug": "annual-general-meeting-2026",
"title": "Volunteer Coordinator",
"excerpt": "string",
"coverImage": "https://cdn.orgo.space/events/12/cover.jpg",
"category": "GENERAL",
"blocks": [
"string"
],
"status": "ACTIVE",
"publishedAt": "2026-01-15T10:30:00+00:00",
"seoTitle": "Annual General Meeting 2026",
"seoDescription": "Quarterly chapter meeting open to all members.",
"dateCreated": "2026-01-15T10:30:00+00:00",
"dateUpdated": "2026-01-15T10:30:00+00:00"
}Create a website article
Creates an article. The tenant is taken from the authenticated user, the slug is normalised and checked for collisions, and the block list is sanitized before storage. Publishing without a date sets it to now, since the public index filters on one. Requires ADMIN_TENANT.
curl --request POST \
--url https://app.orgo.space/api/v1/website_articles \
--header 'Api-Token: <api-key>' \
--header 'Content-Type: application/json' \
--data '
{
"slug": "annual-general-meeting-2026",
"title": "Volunteer Coordinator",
"excerpt": "string",
"coverImage": "https://cdn.orgo.space/events/12/cover.jpg",
"category": "GENERAL",
"blocks": [
"string"
],
"status": "ACTIVE",
"publishedAt": "2026-01-15T10:30:00+00:00",
"seoTitle": "Annual General Meeting 2026",
"seoDescription": "Quarterly chapter meeting open to all members."
}
'import requests
url = "https://app.orgo.space/api/v1/website_articles"
payload = {
"slug": "annual-general-meeting-2026",
"title": "Volunteer Coordinator",
"excerpt": "string",
"coverImage": "https://cdn.orgo.space/events/12/cover.jpg",
"category": "GENERAL",
"blocks": ["string"],
"status": "ACTIVE",
"publishedAt": "2026-01-15T10:30:00+00:00",
"seoTitle": "Annual General Meeting 2026",
"seoDescription": "Quarterly chapter meeting open to all members."
}
headers = {
"Api-Token": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Api-Token': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
slug: 'annual-general-meeting-2026',
title: 'Volunteer Coordinator',
excerpt: 'string',
coverImage: 'https://cdn.orgo.space/events/12/cover.jpg',
category: 'GENERAL',
blocks: ['string'],
status: 'ACTIVE',
publishedAt: '2026-01-15T10:30:00+00:00',
seoTitle: 'Annual General Meeting 2026',
seoDescription: 'Quarterly chapter meeting open to all members.'
})
};
fetch('https://app.orgo.space/api/v1/website_articles', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.orgo.space/api/v1/website_articles",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'slug' => 'annual-general-meeting-2026',
'title' => 'Volunteer Coordinator',
'excerpt' => 'string',
'coverImage' => 'https://cdn.orgo.space/events/12/cover.jpg',
'category' => 'GENERAL',
'blocks' => [
'string'
],
'status' => 'ACTIVE',
'publishedAt' => '2026-01-15T10:30:00+00:00',
'seoTitle' => 'Annual General Meeting 2026',
'seoDescription' => 'Quarterly chapter meeting open to all members.'
]),
CURLOPT_HTTPHEADER => [
"Api-Token: <api-key>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.orgo.space/api/v1/website_articles"
payload := strings.NewReader("{\n \"slug\": \"annual-general-meeting-2026\",\n \"title\": \"Volunteer Coordinator\",\n \"excerpt\": \"string\",\n \"coverImage\": \"https://cdn.orgo.space/events/12/cover.jpg\",\n \"category\": \"GENERAL\",\n \"blocks\": [\n \"string\"\n ],\n \"status\": \"ACTIVE\",\n \"publishedAt\": \"2026-01-15T10:30:00+00:00\",\n \"seoTitle\": \"Annual General Meeting 2026\",\n \"seoDescription\": \"Quarterly chapter meeting open to all members.\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Api-Token", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.orgo.space/api/v1/website_articles")
.header("Api-Token", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"slug\": \"annual-general-meeting-2026\",\n \"title\": \"Volunteer Coordinator\",\n \"excerpt\": \"string\",\n \"coverImage\": \"https://cdn.orgo.space/events/12/cover.jpg\",\n \"category\": \"GENERAL\",\n \"blocks\": [\n \"string\"\n ],\n \"status\": \"ACTIVE\",\n \"publishedAt\": \"2026-01-15T10:30:00+00:00\",\n \"seoTitle\": \"Annual General Meeting 2026\",\n \"seoDescription\": \"Quarterly chapter meeting open to all members.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.orgo.space/api/v1/website_articles")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Api-Token"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"slug\": \"annual-general-meeting-2026\",\n \"title\": \"Volunteer Coordinator\",\n \"excerpt\": \"string\",\n \"coverImage\": \"https://cdn.orgo.space/events/12/cover.jpg\",\n \"category\": \"GENERAL\",\n \"blocks\": [\n \"string\"\n ],\n \"status\": \"ACTIVE\",\n \"publishedAt\": \"2026-01-15T10:30:00+00:00\",\n \"seoTitle\": \"Annual General Meeting 2026\",\n \"seoDescription\": \"Quarterly chapter meeting open to all members.\"\n}"
response = http.request(request)
puts response.read_body{
"uuid": "01938d8e-9c4f-7c2a-b8e1-3f7a9b8c4f12",
"slug": "annual-general-meeting-2026",
"title": "Volunteer Coordinator",
"excerpt": "string",
"coverImage": "https://cdn.orgo.space/events/12/cover.jpg",
"category": "GENERAL",
"blocks": [
"string"
],
"status": "ACTIVE",
"publishedAt": "2026-01-15T10:30:00+00:00",
"seoTitle": "Annual General Meeting 2026",
"seoDescription": "Quarterly chapter meeting open to all members.",
"dateCreated": "2026-01-15T10:30:00+00:00",
"dateUpdated": "2026-01-15T10:30:00+00:00"
}Authorizations
Server-to-server authentication. Generate a token in the admin UI at
Settings → Developers → API Access. Send the raw token in the
Api-Token header — there is no Bearer prefix.
Tokens can be marked read-only at creation time, in which case the API
rejects any non-GET request with 403 Forbidden.
Body
The new WebsiteArticle resource
One article on a tenant's public website: news, an update, a story.
Every organisation this builder is meant to hold publishes these. All four reference sites carry a news index and article pages, and until now the builder had no answer: an admin could make a page per article, which is a page list nobody can maintain past a dozen, and a "latest news" row that would have to be rebuilt by hand every time.
This is deliberately not App\Entity\Post. That is the members' feed - written by anyone, moderated, reacted to, and scoped to what a member may see. An article is editorial: written by an admin, addressed to the public, and part of the site rather than of the community. Sharing a table would mean every query for one carried the other's rules.
The body is the same block list a page has, through the same sanitizer, so everything the catalogue can express is available in an article - and an agent that has learned the block vocabulary already knows how to write one.
Response
WebsiteArticle resource created
One article on a tenant's public website: news, an update, a story.
Every organisation this builder is meant to hold publishes these. All four reference sites carry a news index and article pages, and until now the builder had no answer: an admin could make a page per article, which is a page list nobody can maintain past a dozen, and a "latest news" row that would have to be rebuilt by hand every time.
This is deliberately not App\Entity\Post. That is the members' feed - written by anyone, moderated, reacted to, and scoped to what a member may see. An article is editorial: written by an admin, addressed to the public, and part of the site rather than of the community. Sharing a table would mean every query for one carried the other's rules.
The body is the same block list a page has, through the same sanitizer, so everything the catalogue can express is available in an article - and an agent that has learned the block vocabulary already knows how to write one.

