API Guide

Rin exposes an API-key based interface that lets scripts or AI agents manage site content and settings without a login session. Every endpoint lives under /api/key/.

Creating an API key

Only administrators can create API keys. Sign in as an administrator, open the admin Settings page, and scroll to the API keys section at the bottom to create, edit, rotate, and delete keys.

Each key belongs to the administrator who created it, and other accounts never see those keys.

When creating a key you choose:

  • Name: a label you can edit at any time.
  • Scopes: the capabilities this key may use. Each feature area is a separate scope.
  • Storage mode:
    • Encrypted (default): the key is shown once at creation, then only rotation is available.
    • Revealable: the key is stored encrypted and can be revealed and copied at any time.

Storage mode is fixed at creation. An encrypted key can never become revealable; a revealable key can have that capability turned off at any time, and the key keeps working, it just stops showing the plaintext. Create a new key if you need one you can reveal later.

Making requests

Send the key in a request header. Both forms are equivalent:

# X-API-Key header
curl -H "X-API-Key: <your-key>" https://your-site/api/key/help
# Authorization header
curl -H "Authorization: Bearer <your-key>" https://your-site/api/key/help

Keys are 32 characters. A missing or invalid key returns 401; a valid key without the required scope returns 403.

Inspecting the current key

curl -H "X-API-Key: <your-key>" https://your-site/api/key/help

The response lists the key's scopes and the endpoints available for each one:

{
  "success": true,
  "key_id": 1,
  "scopes": ["feed.write"],
  "permissions": [
    {
      "scope": "feed.write",
      "title": "Article management",
      "description": "Create, edit and delete articles through the API.",
      "endpoints": ["POST /api/key/feed", "POST /api/key/feed/:id", "DELETE /api/key/feed/:id"]
    }
  ],
  "base_path": "/api/key"
}

Scopes and endpoints

ScopePurposeEndpoints
feed.writeArticlesPOST /api/key/feed, POST /api/key/feed/:id, DELETE /api/key/feed/:id
friend.writeFriend linksPOST /api/key/friend, PUT /api/key/friend/:id, DELETE /api/key/friend/:id
moment.writeMomentsPOST /api/key/moment, POST /api/key/moment/:id, DELETE /api/key/moment/:id
config.siteSite infoGET/POST /api/key/config/site
config.personalizationPersonalizationGET/POST /api/key/config/personalization
config.otherOther settings (excluding favicon)GET/POST /api/key/config/other
config.commentComment managementGET/POST /api/key/config/comment
config.supportlySupportly widgetGET/POST /api/key/config/supportly
config.webhookWebhook and test webhookGET/POST /api/key/config/webhook, POST /api/key/config/webhook/test
config.friendFriend link settingsGET/POST /api/key/config/friend
config.maintenanceMaintenanceGET/POST /api/key/config/maintenance, DELETE /api/key/config/cache
config.aiAI settingsGET/POST /api/key/config/ai

Articles

Create an article

curl -X POST https://your-site/api/key/feed \
  -H "X-API-Key: <your-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Article title",
    "content": "# Markdown body",
    "summary": "Summary",
    "tags": ["Rin"],
    "listed": true,
    "draft": false
  }'
FieldTypeRequiredDescription
titlestringyesArticle title
contentstringyesMarkdown body
summarystringnoSummary
aliasstringnoCustom URL alias
tagsstring[]noTag list
listedbooleannoShow in listings, defaults to true
draftbooleannoDraft flag, defaults to true
createdAtstringnoPublish time, ISO 8601

Edit an article

curl -X POST https://your-site/api/key/feed/12 \
  -H "X-API-Key: <your-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "New title",
    "content": "Updated body",
    "draft": false
  }'

Only send the fields you want to change; omitted fields keep their current values.

Delete an article

curl -X DELETE https://your-site/api/key/feed/12 \
  -H "X-API-Key: <your-key>"
curl -X POST https://your-site/api/key/friend \
  -H "X-API-Key: <your-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Friend site",
    "desc": "Short description",
    "avatar": "https://example.com/avatar.png",
    "url": "https://example.com",
    "group": "default"
  }'
FieldTypeRequiredDescription
namestringyesSite name, max 20 characters
descstringyesDescription, max 100 characters
avatarstringyesAvatar URL, max 100 characters
urlstringyesSite URL, max 100 characters
groupstringnoGroup name, max 20 characters
acceptedbooleannoMark as approved, defaults to true
sort_ordernumbernoSort value, defaults to 0
curl -X PUT https://your-site/api/key/friend/3 \
  -H "X-API-Key: <your-key>" \
  -H "Content-Type: application/json" \
  -d '{ "desc": "Updated description" }'
curl -X DELETE https://your-site/api/key/friend/3 \
  -H "X-API-Key: <your-key>"

Moments

Create a moment

curl -X POST https://your-site/api/key/moment \
  -H "X-API-Key: <your-key>" \
  -H "Content-Type: application/json" \
  -d '{ "content": "Nice weather today" }'

Edit a moment

curl -X POST https://your-site/api/key/moment/5 \
  -H "X-API-Key: <your-key>" \
  -H "Content-Type: application/json" \
  -d '{ "content": "Updated content" }'

Delete a moment

curl -X DELETE https://your-site/api/key/moment/5 \
  -H "X-API-Key: <your-key>"

Settings

Settings endpoints follow GET /api/key/config/{section} to read and POST /api/key/config/{section} to write. A write may only contain keys allowed for that section; out-of-range keys make the request fail with 400 and the rejected key names.

curl -X POST https://your-site/api/key/config/site \
  -H "X-API-Key: <your-key>" \
  -H "Content-Type: application/json" \
  -d '{ "site.name": "New name", "site.description": "New description" }'

Writable keys by section

SectionScopeWritable keys
siteconfig.sitesite.name, site.description, site.avatar, site.page_size, announcement.content, announcement.speed
personalizationconfig.personalizationheader.layout, header.behavior, feed.layout, feed.card_variant, theme.color
otherconfig.otherlogin.enabled, counter.enabled, rss, footer, cache.enabled
commentconfig.commentcomment.enabled, comment.default, comment.systemOrder, twikoo.*, giscus.*, waline.*, cwd.*, gitalk.*, utterances.*, discuss.*
supportlyconfig.supportlysupportly.enabled, supportly.channelId, supportly.title, supportly.widgetUrl
webhookconfig.webhookwebhook_url, webhook.method, webhook.content_type, webhook.headers, webhook.body_template
friendconfig.friendfriend_apply_enable, friend_groups, friend_group_order, friend_crontab, friend_ua
maintenanceconfig.maintenancecache.enabled
aiconfig.aiai_search.enabled, ai_summary.enabled, ai_summary.provider, ai_summary.model, ai_summary.api_url, ai_summary.retries, ai_summary.api_key, ai_summary.failover, ai_summary.custom_code

The favicon is not writable through any section.

Testing the webhook

curl -X POST https://your-site/api/key/config/webhook/test \
  -H "X-API-Key: <your-key>" \
  -H "Content-Type: application/json" \
  -d '{ "test_message": "Test message" }'

Values such as webhook_url in the body override the saved configuration for this single test only.

Clearing the cache

curl -X DELETE https://your-site/api/key/config/cache \
  -H "X-API-Key: <your-key>"

Content ownership

Articles and moments created through the API are attributed to user ID 1. To use a different account, change API_KEY_CONTENT_OWNER_UID in server/src/services/api-key-owner.ts and redeploy.

Error responses

{
  "success": false,
  "error": "This API key is missing the \"feed.write\" scope"
}
StatusMeaning
400Invalid parameters, or a config key outside the granted section
401Missing, invalid, rotated or deleted API key
403Valid key without the required scope
404Resource or config section not found

Security notes

  • Create one key per use case and grant only the scopes it needs.
  • Never ship a key in browser code or a public repository.
  • Rotate or delete a key immediately if you suspect a leak.
  • Delete keys you no longer use.