Best practices for API design, implementation, and content management in Kuroco HeadlessCMS.
Documentation Reference: /kuroco-docs You can search and reference the official Kuroco documentation using the skill.
Table of Contents
Part 1: API Integration Patterns
- Endpoint Design
- Security Settings
- Cache Strategy
- Rate Limiting
- API Call Patterns
- Error Handling
Part 2: Content Management Patterns
- Content Structure
- Extended Fields (Custom Fields)
- Topics API Operations
- Content CRUD Operations
- Filter Query → See references/filter-query.md
- File/CSV Operations → See references/file-operations.md
- Management Operations via Admin MCP (Content)
Part 1: API Integration Patterns
Endpoint Design
Basic Structure
The Kuroco API path has the following format:
https://{サイトキー}.g.kuroco.app/rcms-api/{api_id}/{endpoint_path}
Example:
https://example.g.kuroco.app/rcms-api/1/news
https://example.g.kuroco.app/rcms-api/1/member/login
Main Endpoint Configuration Items
| Item | Description | Example |
|---|---|---|
| Path | Endpoint URL | news, member/list |
| Model | Target of Operation | Topics, Member, InquiryForm |
| Operation | Operation Type | list, details, insert, update, delete |
| Cache | Response Cache Duration | 86400 (1 day) |
| Rate Limiting | Request Count Limit | 100 times/minute |
| Authentication Required | Whether login is required | true/false |
Main Categories and Models
Authentication
| Operation | Description | Method |
|---|---|---|
login_challenge |
Login | POST |
token |
Access Token Acquisition | POST |
logout |
Logout | POST |
profile |
Logged-in User Information Acquisition | GET |
reminder |
Password Reminder | POST |
Content (Topics)
| Operation | Description | Method |
|---|---|---|
list |
List Acquisition | GET |
details |
Details Acquisition | GET |
insert |
New Addition | POST |
update |
Update | POST |
delete |
Delete | POST |
bulk_upsert |
Bulk Update | POST |
Member (Member)
| Operation | Description | Method |
|---|---|---|
list |
Member List | GET |
details |
Member Details | GET |
insert |
Member Registration | POST |
update |
Member Update | POST |
Form (InquiryMessage/InquiryForm)
| Operation | Description | Method |
|---|---|---|
send |
Form Submission | POST |
list |
Response List | GET |
details |
Response Details | GET |
EC Points (ECPoint)
| Operation | Description | Method |
|---|---|---|
update |
Granting/Consuming Points | POST |
history |
Acquisition of Point History/Current Balance | GET |
Security Settings
Detailed Reference: For details on security settings, see references/security-settings.md .
Official Documentation: For detailed official information, see
../kuroco-docs/docs/management-api.md(api-security), etc.
Authentication Methods
Admin panel: Select from four types in [API] → [Security].
| Authentication method | Use case | Header |
|---|---|---|
| None | Public data-only API (used together with cache). Do not use for private data or write operations. | Not required |
| Static access token | Server-to-server communication, public API | X-RCMS-API-ACCESS-TOKEN: {固定トークン} |
| Dynamic access token | Login-required sites (JWT) | X-RCMS-API-ACCESS-TOKEN: {動的トークン} |
| Cookie | Login-required website | credentials: 'include'(frontend side) |
Advance notice of token expiration: For static access tokens, privileged static tokens, and KurocoFront tokens, notification emails are sent to the email address in account settings 30 days, 7 days, and 1 day before expiration.
Note: Changing the API security settings invalidates existing issued tokens (a confirmation alert is displayed when changing). If there are clients in operation, plan to reissue and replace tokens in advance.
1. Cookie authentication (recommended for web apps)
Session-based authentication.credentials: 'include' is required.
// ログイン
const response = await fetch('https://example.g.kuroco.app/rcms-api/1/login', {
method: 'POST',
credentials: 'include', // 必須
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: 'user@example.com',
password: 'password123'
})
})
// レスポンス例
// {
// "grant_token": "xxxxx",
// "status": 0,
// "member_id": 123
// }
Notes:
- Third-party cookie issue (blocked by Safari, etc.)
- The API domain and frontend must be set to **the same domain (different subdomains)** (first-party cookie configuration)
- Example:
api.example.comとwww.example.com
- Example:
- When there are multiple Cookie authentication APIs, the authentication state isshared
2. Token authentication (recommended for mobile apps)
JWT-based authentication. Attach the token to the header.
The acquisition flow has two steps: First, login(Login::login_challenge) by POSTing email/password to grant_token and then exchange it for token(Login::token) to the endpoint by POSTing grant_token and access_token / refresh_token .token You cannot send email/password directly to theendpoint (the accepted body is { "grant_token": "..." }on initial issuance and { "refresh_token": "..." } only).
// 1. ログインして grant_token を取得
const loginResponse = await fetch('https://example.g.kuroco.app/rcms-api/1/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: 'user@example.com',
password: 'password123'
})
})
const { grant_token } = await loginResponse.json()
// 2. grant_token をアクセストークンに交換
const tokenResponse = await fetch('https://example.g.kuroco.app/rcms-api/1/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ grant_token })
})
const { access_token, refresh_token } = await tokenResponse.json()
// レスポンス例
// {
// "access_token": {
// "value": "eyJhbGciOiJS...",
// "expiresAt": "2024-01-01T12:00:00+09:00"
// },
// "refresh_token": {
// "value": "xxxxxx",
// "expiresAt": "2024-01-08T12:00:00+09:00"
// }
// }
// API呼び出し時
const response = await fetch('https://example.g.kuroco.app/rcms-api/1/news', {
headers: {
'X-RCMS-API-ACCESS-TOKEN': access_token.value
}
})
Prerequisites: At least one user,login_challenge + token endpoint required. When there are multiple token authentication APIs, the authentication state isnot shared。
3. StaticToken authentication (server-to-server communication)
API access restriction using a fixed token.
const response = await fetch('https://example.g.kuroco.app/rcms-api/1/internal-api', {
headers: {
'X-RCMS-API-ACCESS-TOKEN': 'your-static-token-here'
}
})
Configuration location: Admin panel → API → Security → StaticToken
Note: Static tokens are visible to users if embedded in the frontend. Operation must assume token renewal in case of leakage.
IP address restriction
Admin panel: [API] → [Security] → [IP address restriction]
Allow access only from specified IP addresses.
| Specification format | Example | Description |
|---|---|---|
| Individual IP | 192.0.2.1 |
Single IP address |
| CIDR | 192.0.2.0/24 |
Subnet unit |
| Range specification | 192.0.2.1-192.0.2.2 |
IP range by hyphen |
IP Address Group: Define IPSETS_* as a constant function, and[[IPSETS_*]] can be referenced with../kuroco-docs/docs/faq-domain.md(is-it-possible-to-set-multiple-ip-addresses-at-once) reference).
CORS Configuration
Admin panel: [API] → [Security] → [CORS]
| Item | Supported headers | Description |
|---|---|---|
| CORS_ALLOW_ORIGINS | Access-Control-Allow-Origin | Allowed origins (Wildcard*deprecated)) |
| CORS_ALLOW_METHODS | Access-Control-Allow-Methods | Allowed HTTP methods |
| CORS_ALLOW_HEADERS | Access-Control-Allow-Headers | Allowed request headers |
| CORS_MAX_AGE | Access-Control-Max-Age | Preflight cache seconds |
| CORS_ALLOW_CREDENTIALS | Access-Control-Allow-Credentials | Allow cookie sending |
CSRF Protection: CORS + Content-Type: application/json protects against CSRF attacks in modern browsers. Using wildcards eliminates the protective effect, so always specify a specific domain.
Delay in reflecting changes: After changing CORS settings, they are cached by the browser for CORS_MAX_AGE minutes. For immediate reflection, clear the browser cache or set CORS_MAX_AGE を 0 .
API Request Restrictions
| Restriction type | Description |
|---|---|
| None | No restriction |
| GroupAuth | Logged-in user'sGroup permissionsand allow only if they match |
| MemberCustomSearchAuth | Logged-in userCustom Member FilterAllow only if the conditions of |
Priority order of access restrictions
Restrictions when returning content are evaluated in the following order (higher priority first):
- API → IP address restriction(entire API)
- Endpoint → API request restriction(per endpoint)
- Content definition → API request restriction
- Content category → API request restriction
- Individual content → API request restriction
Response restrictions via post-processing
Admin panel: [API] → Endpoint → [Post-processing]
Exclude unnecessary fields from API responses to control published information:
| Processing type | Description |
|---|---|
| Output allowlist | Return only specified fields (whitelist). Example: list.subject, pageInfo |
| Output transformation list | Field deletion, renaming, application of transformation functions |
| Custom processing | Custom logic using Smarty templates |
Performance Tip: Placing the output allowlist before other processing makes it effective at the SQL level.
Platform Security
Infrastructure-level security provided by the Kuroco platform:
- Communication: Full HTTPS encryption, automatic TLS certificate management
- Defense: WAF, CDN, DDoS protection (Fastly DDoS Protection as an option)
- Authentication integration: SAML/OAuth external login, client certificates (optional)
- Audit: Access logs, application logs
- Certification: ISMS (ISO 27001), ISMS Cloud (ISO 27017), PrivacyMark
- Diagnostics: Daily container vulnerability scanning, VADDY-integrated automatic diagnostics
Configuration changes via Admin MCP
API security settings can be configured not only from the admin screen but alsovia Admin MCP. For connection and authentication, refer to the /kuroco-admin-mcp skill.
Cache strategy
Recommended settings
| Use Case | Cache duration | Setting value |
|---|---|---|
| Static content (news, etc.) | 1 day | 86400 |
| Content with low update frequency | 1 week | 604800 |
| Requires real-time behavior | No cache | 0 |
| APIs requiring authentication | No cache | 0 |
Important: When data such as content or members is updated, the cache is automatically cleared.The range that gets cleared is determined by the type of data, so understanding the following makes it easier to judge whether it is safe to set a longer cache duration.
Topics cache clear range
| Operation | Range cleared |
|---|---|
Topics(insert / update / delete / accept / bulk_upsert / increment) |
All endpoints tied to the same content definition group (TopicsGroup).Topics::details, only the cache for the updated content ID is cleared |
| Content categories (create, update, delete) | All endpoints tied to the same content definition group.Topics::detailsis not covered(category changes do not clear the details side) |
Form (InquiryForm insert/update/delete, answer CSV upload) |
All endpoints tied to the same form ID |
Comment (Comment の insert/update/delete) |
All endpoints tied to the model category "Activity" + endpoints tied to the linked content definition group |
Favorites (Favorite の insert/delete) |
All endpoints of the target model category + tied to the content definition of the target contentTopics::listEndpoint |
Member (Member の insert/update) |
All endpoints tied to the model categories "Member" and "Authentication" |
| Master (update/delete) | All endpoints tied to the same master ID |
Operations other than the above are not automatically cleared. If data is updated via routes other than the standard Topics API, such as through a proxy or custom Smarty processing for ingestion, it is not subject to automatic clearing, so explicitly specify the clear range and timing with the Smarty plugin purge_cdn_cache (e.g., after Favorite::insertin post-processing, deleteTopics::details's cache). If you need to clear immediately, you can also clear manually with the "Clear cache" button in the endpoint list.
Source: ../kuroco-docs/docs/reference-api-1.md(cache-clear-operation)
Cache Header
Can be checked in the response header:
Cache-Control: max-age=86400
Rate limiting
Restrictions for free-tier users
Users who have not registered payment information are subject to the following restrictions. This does not apply to users who have registered a credit card or use invoice payment.
| Target | Restriction |
|---|---|
| API sending | Approximately 1,000 per hour |
| Batch processing | Approximately 100 per hour |
Response Header
x-rcms-ratelimit-limit: 100 # 制限数
x-rcms-ratelimit-remaining: 95 # 残りリクエスト数
x-rcms-ratelimit-reset: 60 # リセットまでの秒数
Handling 429 errors
const response = await fetch(url)
if (response.status === 429) {
const resetTime = response.headers.get('x-rcms-ratelimit-reset')
throw new Error(`流量制限超過。${resetTime}秒後に再試行してください`)
}
API call patterns
List retrieval (with pagination)
async function fetchNewsList(page = 1, perPage = 10) {
const params = new URLSearchParams({
pageID: page,
cnt: perPage
})
const response = await fetch(
`https://example.g.kuroco.app/rcms-api/1/news?${params}`,
{ credentials: 'include' }
)
const data = await response.json()
// レスポンス構造
// {
// "list": [...],
// "pageInfo": {
// "totalCnt": 100,
// "perPage": 10,
// "totalPageCnt": 10,
// "pageNo": 1
// }
// }
return data
}
Filter search
// filter パラメータで検索
const params = new URLSearchParams({
filter: 'subject contains "重要"',
order_query: 'ymd=DESC'
})
// カテゴリでの絞り込みは contents_type を使う(category_id というフィールドは無い)
// 例: filter: 'contents_type = 1'
const response = await fetch(
`https://example.g.kuroco.app/rcms-api/1/news?${params}`,
{ credentials: 'include' }
)
Detail retrieval
async function fetchNewsDetail(topicsId) {
const response = await fetch(
`https://example.g.kuroco.app/rcms-api/1/newsdetail/${topicsId}`,
{ credentials: 'include' }
)
const data = await response.json()
// レスポンス構造
// {
// "details": {
// "topics_id": 1,
// "subject": "タイトル",
// "contents": "<p>本文</p>",
// ...
// }
// }
return data.details
}
Bulk retrieval of publication status on multilingual sites
At the Topics::list / Topics::details endpoint, langs_open_flg When the parameter is enabled, each content includes an langs_open_flg object indicating the publication status for all configured languages. This allows controlling the display of the language switcher UI without making separate API requests for each language.
// レスポンス例
// {
// "details": {
// "topics_id": 1,
// "langs_open_flg": { "en": 1, "ja": 0 },
// ...
// }
// }
Retrieval and sorting of favorite date and time
At the Topics::list endpoint, get_last_favorite_ymdhi When the parameter is enabled, you can retrieve the date and time when each content was last favorited (last_favorite_ymdhi).last_favorite_ymdhi Sorting by this is also supported, enabling list display in "popularity order (most recently favorited first)".
Content creation
async function createNews(newsData) {
const response = await fetch(
'https://example.g.kuroco.app/rcms-api/1/news/insert',
{
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
subject: newsData.title,
contents: newsData.body,
ymd: newsData.date,
open_flg: 1 // 1: 公開, 0: 非公開(topics_flg は「一覧に載せる/載せない」の別フラグ)
})
}
)
return response.json()
}
Error Handling
Major error codes
| Code | Description | Action |
|---|---|---|
| 400 | Request Error | Check request parameters |
| 401 | Authentication error | Check login status and token |
| 403 | Permission error | Check API permission settings |
| 404 | Resource not found | Check path and ID |
| 429 | Rate limit exceeded | Wait before retrying |
| 500 | Server Error | Contact Kuroco support |
Error response example
{
"errors": [
{
"code": "authentication_error",
"message": "ログインが必要です"
}
]
}
Error handling implementation
async function apiRequest(url, options = {}) {
const response = await fetch(url, {
credentials: 'include',
...options
})
if (!response.ok) {
const errorData = await response.json()
switch (response.status) {
case 401:
throw new Error('認証が必要です')
case 403:
throw new Error('アクセス権限がありません')
case 429:
throw new Error('リクエスト制限を超えました')
default:
throw new Error(errorData.errors?.[0]?.message || 'APIエラー')
}
}
return response.json()
}
Part 2: Content management patterns
Content structure
Hierarchical structure
コンテンツ定義(TopicsGroup)
├── カテゴリ(TopicsCategory)
│ └── コンテンツ(Topics)
└── 拡張項目(ext_col_01〜ext_col_XX)
Content definition settings
Admin panel: [Content definition] → [Create new]
| Item | Description |
|---|---|
| Group name | Name of the content definition |
| Identifier | Unique ID (alphanumeric) |
| Body input method | WYSIWYG, Markdown, HTML |
| Viewing restriction | Everyone/Group restriction/Custom search |
| Editing restriction | Everyone/Group restriction/Custom search |
| Extended items | Custom fields. The upper limit iswhoamiのsite.limits.topics_max_extension({current, max}).maxis 999 for JSONB format sites and 99 for legacy format sites.currentがmaxIf less thanadmin_setting-updatecan be increased with (details: /kuroco-admin-mcp) |
Extended items (custom fields)
Do not guess key names. The key names of extended items areper site, divided into
ext_1format andext_col_01format。 If you can connect to Admin MCP,whoamiのsite.topics_ext_key_formatto check the naming for that site, and determine which number corresponds to which item withtopics-describe(the group-unit correct answer) (→/kuroco-admin-mcp)。ext_slugitems configured with this use the slug as the key, so they are not affected by this branch. Configuration is recommended. The table below is an example for aext_col_01format site.
| Type | Description | API response example |
|---|---|---|
| Text | Single-line text | "ext_col_01": "値" |
| Textarea | Multi-line text | "ext_col_02": "複数行\nテキスト" |
| WYSIWYG | Rich Text | "ext_col_03": "<p>HTML</p>" |
| Number | Integer/decimal | "ext_col_04": 100 |
| Date | Date picker | "ext_col_05": "2024-01-01" |
| Select (single) | Radio Button | "ext_col_06": "選択肢1" |
| Select (multiple) | Checkbox | "ext_col_07": ["選択肢1", "選択肢2"] |
| File/Image | Upload | "ext_col_08": { "id": "xxx", "url": "https://...", "desc": "" } |
| Link | URL Link | "ext_col_10": { "url": "https://...", "title": "リンク名" } |
| Related content | Reference to other content | "ext_col_11": { "topics_id": 123, "subject": "タイトル" } |
Topics API Operations
| Operation | Description | Method | Path example |
|---|---|---|---|
| list | List retrieval | GET | /news |
| details | Detail retrieval | GET | /newsdetail/{topics_id} |
| insert | Create new | POST | /news/insert |
| update | Update | POST | /news/update/{topics_id} |
| delete | Delete | POST | /news/delete/{topics_id} |
| bulk_upsert | Bulk update | POST | /news/bulk |
Content CRUD operations
List retrieval response
{
"list": [
{
"topics_id": 1,
"subject": "タイトル",
"contents": "本文(HTML)",
"ymd": "2024-01-01",
"open_flg": 1,
"contents_type": 1,
"contents_type_nm": "カテゴリ名",
"ext_col_01": "拡張項目値",
"tag": ["タグ1", "タグ2"]
}
],
"pageInfo": {
"totalCnt": 100,
"perPage": 10,
"totalPageCnt": 10,
"pageNo": 1
}
}
Content creation
const response = await fetch('/rcms-api/1/news/insert', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
subject: 'タイトル',
contents: '<p>本文</p>',
ymd: '2024-01-01',
open_flg: 1, // 1: 公開, 0: 非公開
contents_type: 1, // カテゴリID(category_idというフィールドは無い)
open_type: 'sitei', // 予約公開を使う場合は 'sitei' を明示(既定 'open' のままだと日時が保存されない)
open_sta_date: '2024-12-01 00:00:00', // 公開開始日時
open_end_date: '2024-12-31 23:59:59', // 公開終了日時
tag: ['タグ1', 'タグ2'],
ext_col_01: 'カスタム値'
})
})
Content update
const response = await fetch(`/rcms-api/1/news/update/${topicsId}`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
subject: '更新タイトル',
contents: '更新本文'
// 更新したいフィールドのみ送信可能
})
})
Content deletion
await fetch(`/rcms-api/1/news/delete/${topicsId}`, {
method: 'POST',
credentials: 'include'
})
Filter Query
Basic syntax: filter={field} {operator} {value}
| Operator | Example |
|---|---|
=, != |
filter=contents_type = 1 |
>, >=, <, <= |
filter=ymd >= "2024-01-01" |
contains, ncontains |
filter=subject contains "キーワード" |
in, nin |
filter=contents_type in [1, 2, 3] |
String values are enclosed in double quotes. Single quotes are treated as part of the value including the quote characters, and instead of causing an error they result in 0 results (if the value contains a space, it is a syntax error).
The field name for the category is contents_type(category_id field does not exist).
Compound condition: filter=(contents_type = 1 or contents_type = 2) and topics_flg = 1
Sort: order_query=ymd=DESC({フィールド}={ASC|DESC}. Separate multiple values with commas)
Detailed usage: references/filter-query.md See
File/CSV operations
File Upload
// 1. ファイルアップロード
const formData = new FormData()
formData.append('file', file)
const result = await fetch('/rcms-api/1/files/upload', {
method: 'POST',
credentials: 'include',
body: formData
})
const { file_id } = await result.json()
// 2. コンテンツに紐付け
await fetch('/rcms-api/1/news/insert', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
subject: 'タイトル',
ext_col_02: { file_id, desc: '説明' }
})
})
Details (bulk update, category, tags, access restrictions): references/file-operations.md See
Management operations via Admin MCP (content)
To perform content operations equivalent to the admin panel from an AI agent (an MCP client such as Claude Desktop / claude.ai / Claude Code), use Admin MCP(/kuroco-admin-mcp skills) . Note the difference from the frontend API (rcms-api).
Frontend API vs management operations (Admin MCP)
| Item | Frontend API (rcms-api) | Management operations (Admin MCP) |
|---|---|---|
| Target | End User | Administrators/operators |
| Authentication | StaticToken / DynamicToken / Cookie | OAuth(Admin MCP) |
| Endpoint | /rcms-api/{api_id}/{path} |
Via Admin MCP tools |
| Use case | Frontend implementation | Bulk data operations, structure checks, configuration changes |
Use cases
- Understanding site structure: Check the content definition list and extended items
- Bulk checking and correcting data: Operate on data efficiently without going through the admin panel GUI
- Schema check: Get field definitions with the Admin MCP schema retrieval tool
Caution: Always confirm insert/update/delete with the user before executing. For connection settings and tool details, see
/kuroco-admin-mcpskill reference.
Best Practices
- Using cache: In the endpoint settings, set
キャッシュ: 86400(1 day). Automatically cleared on update - Pagination:
pageIDとcntFetch in chunks with the parameter
Related skills
/kuroco-frontend-integration- API call patterns in Vite/Nuxt.js/Next.js, KurocoFront deployment/kuroco-server-processing- Smarty plugin/syntax reference, Webhook/batch processing/kuroco-admin-mcp- Admin operations via Admin MCP
Related documents
In-skill reference
- references/security-settings.md - Detailed reference for security settings
- references/filter-query.md - Filter query details
- references/file-operations.md - File/CSV operation details
Kuroco official documentation
../kuroco-docs/docs/management-api.md(api-security) - API security settings (authentication methods, IP restrictions)../kuroco-docs/docs/management-api.md(api-list) - API list/CORS settings../kuroco-docs/docs/management-api.md(api-postprocessing) - API post-processing settings../kuroco-docs/docs/reference-api-2.md(endpoint-settings) - List of endpoint setting items../kuroco-docs/docs/reference-smarty-trigger-1.md(post-processing) - Detailed reference for post-processing../kuroco-docs/docs/tutorials-api-custom-1.md(configure-endpoint) - How to configure endpoints../kuroco-docs/docs/tutorials-auth-member-3.md(login) - Login implementation../kuroco-docs/docs/tutorials-api-custom-1.md(restricting-api-access-with-statictoken) - StaticToken authentication../kuroco-docs/docs/reference-api-1.md(api-cache) - API Cache../kuroco-docs/docs/reference-api-1.md(cache-clear-operation) - Timing and scope of API cache clearing../kuroco-docs/docs/reference-api-3.md(filter-query) - Filter Query../kuroco-docs/docs/about.md(security) - Platform security overview../kuroco-docs/docs/faq-member.md(in-what-order-are-viewing-restrictions-applied) - Priority order of access restrictions- https://kuroco.app/ja/docs/faq/cors-and-content-type-prevent-csrf-attacks/ - CSRF countermeasures
../kuroco-docs/docs/faq-api-error.md(the-api-returns-403-forbidden-even-though-no-restrictions-are-applied) - Resolving 403 errors../kuroco-docs/docs/tutorials-content-1.md(adding-a-topics) - Content definition creation../kuroco-docs/docs/tutorials-content-1.md(bulk-upload-in-csv) - CSV Upload../kuroco-docs/docs/management-content.md(content-structure-topics) - Content structure