Αναφορά
REST API παράδοσης
Δύο διαδρομές μόνο για ανάγνωση εξυπηρετούν κάθε συλλογή του χώρου εργασίας σας. Ο ιστότοπός σας τις καλεί με ένα κλειδί API· οι συντάκτες δεν χρειάζεται καν να γνωρίζουν ότι υπάρχουν.
Τελικά σημεία#
Η βασική διεύθυνση URL είναι https://api.console.citronos.com. Το {collection} είναι το slug της συλλογής, δηλαδή το όνομά της στο API μέσα στην κονσόλα. Η μετονομασία του slug αλλάζει τη διαδρομή, γι’ αυτό η κονσόλα σάς προειδοποιεί πριν το επιτρέψει.
GET/v1/content/{collection}
Οι δημοσιευμένες εγγραφές μιας συλλογής, μία σελίδα κάθε φορά. Για μια συλλογή μοναδικού εγγράφου επιστρέφει το ίδιο το έγγραφο.
GET/v1/content/{collection}/{slugOrId}
Μία δημοσιευμένη εγγραφή, βάσει slug ή αναγνωριστικού.
Έλεγχος ταυτότητας#
Στείλτε ένα κλειδί παράδοσης στο Authorization: Bearer … ή στο x-api-key. Δημιουργήστε κλειδιά στην κονσόλα, στις Ρυθμίσεις → Προγραμματιστές → Κλειδιά API. Το πλήρες κλειδί εμφανίζεται μία μόνο φορά· το Citron κρατά μόνο το hash του.
| Κλειδί | Διαβάζει | Πού χρησιμοποιείται |
|---|---|---|
ck_pub_… | Δημοσιευμένο περιεχόμενο | Σε διακομιστή, στο edge ή κατά το build. Με όριο αιτημάτων ανά κλειδί. |
ck_prev_… | Δημοσιευμένο περιεχόμενο και πρόχειρα (status=draft) | Μόνο σε διακομιστή που ελέγχετε. Μην το στέλνετε ποτέ σε πρόγραμμα περιήγησης. |
Ένα κλειδί που λείπει, είναι άγνωστο ή έχει ανακληθεί λαμβάνει απάντηση 401.
# Either header works
curl https://api.console.citronos.com/v1/content/products \
-H "Authorization: Bearer $CITRON_API_KEY"
curl https://api.console.citronos.com/v1/content/products \
-H "x-api-key: $CITRON_API_KEY"Λίστα εγγραφών#
curl "https://api.console.citronos.com/v1/content/products?locale=bg&populate=related&richtext=html" \
-H "Authorization: Bearer $CITRON_API_KEY"const { data, meta, nextCursor } = await citron.collection('products').list({
locale: 'bg',
populate: ['related'],
richtext: 'html',
});
for (const product of data) console.log(product.slug, product.data.name);
console.log(`page ${meta.page} of ${meta.pageCount}`, nextCursor);{
"data": [
{
"id": "cmf3k9x2p0004s601q8w7e2rt",
"slug": "bio-6",
"publishedAt": "2026-09-21T09:30:00.000Z",
"updatedAt": "2026-09-21T09:30:00.000Z",
"sortOrder": 0,
"data": {
"code": "BIO-6",
"category": "station",
"inCapacityTable": true,
"name": "Пречиствателна станция BIO 6",
"body": "<p>Компактна станция за една къща.</p>",
"capacity": 6,
"specRows": [{ "label": "Капацитет", "value": "6 ЕЖ" }],
"cover": {
"id": "cmf3k7a1b0001s601m2n3b4v5",
"url": "https://cdn.example.com/media/bio-6.webp",
"mimeType": "image/webp",
"width": 1600,
"height": 900,
"alt": "Станция BIO 6",
"variants": { "thumb": "https://cdn.example.com/media/bio-6-thumb.webp" },
"focal": { "x": 0.5, "y": 0.4 }
},
"related": [
{
"id": "cmf3k9x2p0007s601z5x4c3v2",
"slug": "bio-10",
"publishedAt": "2026-09-18T12:00:00.000Z",
"updatedAt": "2026-09-18T12:00:00.000Z",
"sortOrder": 1,
"collection": "products",
"data": {
"code": "BIO-10",
"name": "Пречиствателна станция BIO 10",
"capacity": 10,
"related": ["cmf3k9x2p0004s601q8w7e2rt"]
}
}
]
}
}
],
"meta": { "total": 2, "page": 1, "pageSize": 20, "pageCount": 1 }
}Κάθε εγγραφή έχει id, slug, publishedAt, updatedAt, sortOrder και data, που περιέχει τα πεδία της συλλογής. Το meta περιέχει total, page, pageSize και pageCount. Χωρίς sort ισχύει η σειρά που έχει οριστεί για τη συλλογή στην κονσόλα.
- Τα πεδία εικόνας και πολυμέσων επιστρέφονται ως αρχεία πολυμέσων:
id,url,mimeType,width,height,altκαιvariants(ονομασμένες εκδοχές, όπωςthumb). Τα πεδία εικόνας προσθέτουν τοfocal, την υπόδειξη περικοπής του συντάκτη από 0 έως 1. - Ένα πεδίο συσχέτισης περιέχει το αναγνωριστικό της συσχετισμένης εγγραφής. Αν το δηλώσετε στο
populate, περιέχει αντί γι’ αυτό την ίδια την εγγραφή, μαζί με το slug τηςcollection. Οι εγγραφές που φορτώνονται έτσι είναι ρηχές (οι δικές τους συσχετίσεις μένουν αναγνωριστικά) και είναι πάντα οι δημοσιευμένες εκδόσεις. - Οι ζωντανές συλλογές που διαβάζουν από εξωτερικό σύστημα είναι διαθέσιμες μόνο στην κονσόλα· το API παράδοσης απαντά σε αυτές με
404.
Ανάκτηση μίας εγγραφής#
curl "https://api.console.citronos.com/v1/content/products/bio-6?locale=en&richtext=json" \
-H "Authorization: Bearer $CITRON_API_KEY"// Throws CitronError (status 404) when there is no such published entry
const product = await citron.collection('products').get('bio-6', { locale: 'en', richtext: 'json' });
// Resolves null instead of throwing on 404
const maybe = await citron.collection('products').find('bio-6', { locale: 'en' });
console.log(product.id, maybe?.slug);{
"data": {
"id": "cmf3k9x2p0004s601q8w7e2rt",
"slug": "bio-6",
"publishedAt": "2026-09-21T09:30:00.000Z",
"updatedAt": "2026-09-21T09:30:00.000Z",
"sortOrder": 0,
"data": {
"code": "BIO-6",
"category": "station",
"name": "Treatment plant BIO 6",
"body": {
"type": "doc",
"content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "A compact plant for one house." }] }]
},
"capacity": 6,
"datasheet": {
"id": "cmf3k7a1b0002s601p9o8i7u6",
"url": "https://cdn.example.com/media/bio-6.pdf",
"mimeType": "application/pdf",
"width": 0,
"height": 0,
"alt": "Datasheet",
"variants": {}
},
"related": ["cmf3k9x2p0007s601z5x4c3v2"]
}
}
}Το τελευταίο τμήμα της διαδρομής αντιστοιχίζεται με το slug ή με το αναγνωριστικό. Μια εγγραφή που δεν υπάρχει ή δεν έχει δημοσιευτεί ποτέ λαμβάνει απάντηση 404.
Συλλογές μοναδικού εγγράφου#
Μια συλλογή μοναδικού εγγράφου περιέχει ένα μόνο έγγραφο, όπως τις ρυθμίσεις του ιστοτόπου ή την αρχική σελίδα. Η διεύθυνση λίστας της απαντά με { "data": { … } }, δηλαδή με το έγγραφο αντί για λίστα, και με 404 μέχρι να δημοσιευτεί το έγγραφο.
curl "https://api.console.citronos.com/v1/content/site-settings?locale=bg" \
-H "Authorization: Bearer $CITRON_API_KEY"const settings = await citron.collection('site-settings').single({ locale: 'bg' });
console.log(settings.data);Παράμετροι ερωτήματος#
| Παράμετρος | Τιμές | Σημειώσεις |
|---|---|---|
locale | Μια γλώσσα του χώρου εργασίας, π.χ. bg | Τα μεταφρασμένα πεδία επιστρέφονται μόνο σε αυτή τη γλώσσα. Μια άγνωστη γλώσσα λαμβάνει απάντηση 400. |
filter[field], filter[field][op] | Δείτε τα φίλτρα | Έως 10. |
sort | field ή -field | Ένα πεδίο. publishedAt, createdAt, updatedAt, sortOrder, slug ή οποιοδήποτε πεδίο δεν είναι αντικείμενο ή λίστα. |
page | 1, 2, … | Προεπιλογή 1. |
pageSize | Από 1 έως 100 | Προεπιλογή: το μέγεθος λίστας που έχει οριστεί για τη συλλογή. |
populate | Κλειδιά πεδίων συσχέτισης, χωρισμένα με κόμμα | Έως 10 πεδία συσχέτισης πρώτου επιπέδου. |
fields | Κλειδιά του data, χωρισμένα με κόμμα | Τα id, slug, publishedAt, updatedAt και sortOrder επιστρέφονται πάντα. |
richtext | html, json, both | Προεπιλογή both. |
status | draft | Μόνο με κλειδί ck_prev_· διαφορετικά 403. |
preview | Ένα διακριτικό προεπισκόπησης | Ίδιο με την κεφαλίδα x-citron-preview· δείτε την προεπισκόπηση προχείρων. |
Γλώσσες και μορφοποιημένο κείμενο#
Χωρίς locale, κάθε μεταφρασμένο πεδίο είναι ένας χάρτης με όλες τις γλώσσες σας και το μορφοποιημένο κείμενο έρχεται και στις δύο μορφές. Με locale=bg λαμβάνετε τη βουλγαρική τιμή· όπου αυτή είναι κενή, την τιμή της προεπιλεγμένης γλώσσας.
{
"data": [
{
"id": "cmf3k9x2p0004s601q8w7e2rt",
"slug": "bio-6",
"publishedAt": "2026-09-21T09:30:00.000Z",
"updatedAt": "2026-09-21T09:30:00.000Z",
"sortOrder": 0,
"data": {
"code": "BIO-6",
"name": { "bg": "Пречиствателна станция BIO 6", "en": "Treatment plant BIO 6" },
"body": {
"bg": { "html": "<p>Компактна станция.</p>", "json": { "type": "doc", "content": [] } },
"en": { "html": "<p>A compact plant.</p>", "json": { "type": "doc", "content": [] } }
}
}
}
],
"meta": { "total": 2, "page": 1, "pageSize": 20, "pageCount": 1 }
}Το richtext=html επιστρέφει το μορφοποιημένο κείμενο ως συμβολοσειρά HTML, το richtext=json ως έγγραφο του επεξεργαστή (ProseMirror JSON) και το both, η προεπιλογή, ως { "html", "json" }. Παλαιότερες εγγραφές ενδέχεται να περιέχουν απλή συμβολοσειρά HTML, ό,τι κι αν ζητήσετε.
Φίλτρα#
Το filter[field]=value σημαίνει «ισούται με»· το filter[field][op]=value επιλέγει τελεστή. Μπορείτε να φιλτράρετε με βάση το id, το slug και οποιοδήποτε πεδίο εκτός από πεδία αντικειμένου, γεωγραφικά πεδία και πεδία JSON. Τα φίλτρα συνδυάζονται με λογικό ΚΑΙ. Ένα άγνωστο πεδίο ή ένας τελεστής που δεν υποστηρίζει ο τύπος του πεδίου λαμβάνει απάντηση 400.
| Τύπος πεδίου | Τελεστές | Σημειώσεις |
|---|---|---|
| Κείμενο, επιλογή, ημερομηνία, slug, μορφοποιημένο κείμενο, πολυμέσα, συσχέτιση | eq ne lt lte gt gte contains in notIn | Το contains αναζητά υποσυμβολοσειρά χωρίς διάκριση πεζών-κεφαλαίων. Τα in και notIn δέχονται τιμές χωρισμένες με κόμμα. Η σύγκριση είναι κειμενική, οπότε οι ημερομηνίες ISO ταξινομούνται σωστά. |
| Αριθμός | eq ne lt lte gt gte in notIn | Οι τιμές πρέπει να είναι αριθμοί. |
| Λογική τιμή | eq ne | true ή false. |
| Πολλαπλή επιλογή, πολλαπλή συσχέτιση, λίστα | eq contains | Και οι δύο εντοπίζουν τις εγγραφές των οποίων η λίστα περιέχει την τιμή. |
Ένα μεταφρασμένο πεδίο συγκρίνεται στη γλώσσα που ζητήθηκε με locale ή, αν δεν δοθεί, στην προεπιλεγμένη γλώσσα. Εδώ δεν υπάρχει εναλλακτική γλώσσα: μια εγγραφή χωρίς τιμή σε αυτή τη γλώσσα δεν ταιριάζει.
# -g stops curl from treating [ ] as a glob
curl -g "https://api.console.citronos.com/v1/content/products?locale=en\
&filter[category]=station\
&filter[capacity][gte]=5\
&filter[code][in]=BIO-6,BIO-10\
&sort=-publishedAt&page=1&pageSize=20" \
-H "Authorization: Bearer $CITRON_API_KEY"const { data, meta } = await citron.collection('products').list({
locale: 'en',
filter: {
category: 'station', // filter[category]=station
capacity: { gte: 5 }, // filter[capacity][gte]=5
code: { in: ['BIO-6', 'BIO-10'] }, // filter[code][in]=BIO-6,BIO-10
},
sort: '-publishedAt', // or { field: 'publishedAt', order: 'desc' }
page: 1,
limit: 20, // sent as pageSize
});
console.log(meta.total, data.length);Το curl ερμηνεύει τα [ και ] ως μοτίβο glob. Χρησιμοποιήστε το -g ή κωδικοποιήστε τα ως %5B και %5D.
Σελιδοποίηση#
Οι σελίδες αριθμούνται από το 1 και περιέχουν έως 100 εγγραφές. Σταματήστε όταν το page φτάσει το meta.pageCount. Το SDK προσθέτει έναν αδιαφανή nextCursor και έναν επαναλήπτη all() πάνω στους ίδιους αριθμούς σελίδων.
import type { ListResult } from '@citron/sdk';
// Page by page with the opaque cursor
let cursor: string | null = null;
do {
const page: ListResult<Record<string, unknown>> = await citron.collection('products').list({ limit: 100, cursor });
console.log(page.data.length);
cursor = page.nextCursor;
} while (cursor);
// Or let the SDK walk every page
for await (const product of citron.collection('products').all({ fields: ['code', 'name'] })) {
console.log(product.slug);
}Επιλογή πεδίων#
Το fields κρατά μόνο τα κλειδιά του data που απαριθμείτε, κάνοντας τις σελίδες λίστας μικρότερες. Συνδυάζεται με τα populate και locale.
# Only these data keys (id, slug, publishedAt, updatedAt, sortOrder always come)
curl "https://api.console.citronos.com/v1/content/products?locale=en&fields=code,name,cover" \
-H "Authorization: Bearer $CITRON_API_KEY"Προσωρινή αποθήκευση και ETag#
Οι δημοσιευμένες απαντήσεις φέρουν Cache-Control: public, max-age=60, stale-while-revalidate=300 και ένα ETag. Στείλτε το ETag πίσω στο If-None-Match και μια αμετάβλητη απάντηση θα είναι ένα κενό 304. Οι απαντήσεις με πρόχειρα και προεπισκοπήσεις είναι no-store. Για να εμφανίζονται οι αλλαγές τη στιγμή που ένας συντάκτης δημοσιεύει, κρατήστε την προσωρινή αποθήκευση περισσότερο στη δική σας πλευρά και κάντε επανεπικύρωση με webhooks.
curl -i "https://api.console.citronos.com/v1/content/products?locale=en" \
-H "Authorization: Bearer $CITRON_API_KEY" \
-H 'If-None-Match: "3f1c9e0a8b7d6c5e4f3a2b1c0d9e8f7a6b5c4d3e"'
# HTTP/1.1 304 Not Modified (when nothing changed)Προεπισκόπηση προχείρων#
Υπογεγραμμένοι σύνδεσμοι προεπισκόπησης#
Ορίστε στην κονσόλα μια διεύθυνση URL προεπισκόπησης για τη συλλογή, με οποιοδήποτε από τα σύμβολα κράτησης θέσης {slug}, {id}, {locale}, {token} και {collection}, για παράδειγμα https://example.com/{locale}/products/{slug}?preview={token}. Η επιλογή Άνοιγμα προεπισκόπησης σε μια εγγραφή ανοίγει αυτή τη διεύθυνση με ένα υπογεγραμμένο διακριτικό (cpt1.…). Αν το πρότυπο δεν περιέχει {token}, το διακριτικό προστίθεται ως ?preview=.
Η σελίδα σας επιστρέφει το διακριτικό στην κεφαλίδα x-citron-preview (ή στο ?preview=) μαζί με το συνηθισμένο κλειδί ck_pub_. Τότε το API επιστρέφει τα πρόχειρα μόνο αυτής της συλλογής, χωρίς προσωρινή αποθήκευση. Τα διακριτικά λήγουν από προεπιλογή μετά από 30 λεπτά (το πολύ 24 ώρες) και λειτουργούν μόνο για τον χώρο εργασίας σας και για τη συλλογή για την οποία εκδόθηκαν.
Κεφαλίδα απάντησης X-Citron-Preview | Σημασία |
|---|---|
draft | Το διακριτικό είναι έγκυρο· επιστράφηκαν τα πρόχειρα. |
invalid | Το διακριτικό είναι κακοσχηματισμένο, έχει λήξει ή ανήκει σε άλλο χώρο εργασίας· επιστράφηκε το δημοσιευμένο περιεχόμενο. |
other-collection | Το διακριτικό εκδόθηκε για άλλη συλλογή· επιστράφηκε το δημοσιευμένο περιεχόμενο. |
Ένα λανθασμένο διακριτικό δεν προκαλεί ποτέ σφάλμα: η απάντηση είναι ακριβώς η δημοσιευμένη, οπότε ένας παλιός σύνδεσμος προεπισκόπησης απλώς δείχνει τη ζωντανή σελίδα.
# The token arrives in your preview URL: https://example.com/products/bio-6?preview=cpt1.…
curl -i "https://api.console.citronos.com/v1/content/products/bio-6?locale=en" \
-H "Authorization: Bearer $CITRON_API_KEY" \
-H "x-citron-preview: cpt1.eyJvIjoi….k1Qm…"
# X-Citron-Preview: draft
# Cache-Control: no-storeimport { citronTags } from '@citron/sdk/next';
/** previewToken: the ?preview= value the console put in your preview URL. */
export async function getProduct(slug: string, previewToken?: string) {
return citron.collection('products').find(
slug,
previewToken
? { headers: { 'x-citron-preview': previewToken }, fetchOptions: { cache: 'no-store' } }
: { fetchOptions: { next: { revalidate: 3600, tags: citronTags('products', slug) } } },
);
}Κλειδιά προεπισκόπησης#
Ένα κλειδί ck_prev_ με status=draft (στο SDK, preview: true) διαβάζει τα πρόχειρα όλων των συλλογών. Χρησιμοποιήστε το για προεπισκοπήσεις κατά το build σε διακομιστή που ελέγχετε, ποτέ σε πρόγραμμα περιήγησης.
import { createClient } from '@citron/sdk';
// Server only: a ck_prev_ key reads every draft of the workspace
const drafts = createClient({
baseUrl: 'https://api.console.citronos.com',
apiKey: process.env.CITRON_PREVIEW_KEY, // ck_prev_…
preview: true, // sends status=draft
});
const draft = await drafts.collection('products').find('bio-6');
console.log(draft?.updatedAt);Όρια#
Από προεπιλογή, κάθε κλειδί μπορεί να κάνει 300 αιτήματα ανά λεπτό· τα REST και GraphQL μοιράζονται αυτό το όριο. Δείτε τα σφάλματα και όρια ρυθμού για να δείτε πώς μοιάζει μια απάντηση 429.
Αυτή η αναφορά καλύπτει μόνο το δημόσιο API και ακολουθεί τη σύμβαση συμβατότητας του API: οι δημόσιες αποκρίσεις μόνο αποκτούν νέα πεδία. Η περιγραφή OpenAPI αυτών των διαδρομών βρίσκεται στο /developers/openapi.json.