# SolanaLink Full API Reference > Extended documentation for LLM context import. This file provides comprehensive API details for AI agents interacting with SolanaLink (https://solanalink.jp). --- ## Overview SolanaLink is a Tokyo-based IT consulting company. The platform includes a community blog with Reddit-style engagement (voting, reactions, threaded comments) and AI-powered chat. All content is trilingual (ja/en/zh). **Base URL:** `https://solanalink.jp` --- ## Authentication ### OAuth (Browser) SolanaLink uses NextAuth.js v5 with database sessions. Supported providers: - Google (`/api/auth/signin`) - GitHub (`/api/auth/signin`) - Twitter/X (`/api/auth/signin`) - Apple Sign-In (`/api/auth/signin`) After authentication, a session cookie (`authjs.session-token` or `__Secure-authjs.session-token` in production) is set automatically. ### Native Mobile For Capacitor mobile apps: - `POST /api/auth/native-google` — Send `{ idToken }` from Google Sign-In SDK - `POST /api/auth/native-apple` — Send `{ identityToken, email?, givenName?, familyName? }` Both verify tokens server-side and return a session cookie. ### User Roles | Role | Capabilities | |------|-------------| | `USER` | Vote, comment, react, bookmark | | `AUTHOR` | Above + create/edit own posts | | `MODERATOR` | Above + delete any comment, moderate content | | `ADMIN` | Full access including push notifications | --- ## Content API ### List Posts ``` GET /api/posts?locale={locale}&sort={sort}&page={page}&limit={limit} ``` **Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `locale` | string | `ja` | Content locale: `ja`, `en`, `zh` | | `sort` | string | `new` | Sort: `hot` (trending), `new` (latest), `top` (highest score) | | `page` | int | `1` | Page number | | `limit` | int | `20` | Items per page (max 100) | | `category` | string | -- | Filter by category slug | | `tag` | string | -- | Filter by tag slug | **Response:** ```json { "posts": [ { "id": "uuid", "slug": "my-post-title", "locale": "ja", "title": "Post Title", "excerpt": "Short description...", "featuredImage": "https://...", "status": "PUBLISHED", "author": { "id": "uuid", "name": "Author Name", "image": "url" }, "createdAt": "2026-01-01T00:00:00.000Z", "publishedAt": "2026-01-01T00:00:00.000Z", "upvotes": 42, "downvotes": 3, "score": 39, "hotScore": 1234.56, "commentCount": 7, "viewCount": 150, "categories": [{ "id": "uuid", "slug": "tech", "name": "Technology" }], "tags": [{ "id": "uuid", "slug": "nextjs", "name": "Next.js" }] } ], "pagination": { "page": 1, "limit": 20, "total": 150, "totalPages": 8 } } ``` ### Get Post Detail ``` GET /api/posts/{id} ``` Returns full post including `content` (Markdown or HTML) and `contentFormat`. ### Create Post (Auth Required, AUTHOR+) ``` POST /api/posts Content-Type: application/json { "title": "string (required, max 500)", "content": "string (required, Markdown)", "excerpt": "string (optional)", "locale": "ja | en | zh (default: ja)", "status": "DRAFT | PUBLISHED (default: DRAFT)", "categoryIds": ["uuid"], "tagIds": ["uuid"] } ``` ### Update Post (Auth Required, Owner) ``` PUT /api/posts/{id} Content-Type: application/json { "title": "...", "content": "...", "excerpt": "...", "status": "..." } ``` ### Delete Post (Auth Required, Owner/MODERATOR/ADMIN) ``` DELETE /api/posts/{id} ``` ### Trending Posts ``` GET /api/posts/trending?locale={locale}&limit={limit} ``` Returns top posts by hot score from the last 7 days, plus popular tags from the last 30 days. **Response:** ```json { "trendingPosts": [ { "id": "uuid", "slug": "...", "title": "...", "commentCount": 5, "score": 42 } ], "popularTags": [ { "id": "uuid", "slug": "nextjs", "name": "Next.js", "nameJa": "Next.js", "nameEn": "Next.js", "postCount": 12 } ] } ``` --- ## Voting ### Vote on Post (Auth Required) ``` POST /api/posts/{id}/vote Content-Type: application/json { "value": 1 } ``` `value`: `1` (upvote), `-1` (downvote), `0` (remove vote). **Response:** ```json { "success": true, "upvotes": 43, "downvotes": 3, "score": 40, "userVote": 1 } ``` ### Vote on Comment (Auth Required) ``` POST /api/comments/{id}/vote Content-Type: application/json { "value": 1 } ``` Same schema as post voting. Uses Redis locks to prevent race conditions. ### Get User's Vote ``` GET /api/posts/{id}/vote GET /api/comments/{id}/vote ``` Returns `{ "userVote": 0 | 1 | -1 }`. --- ## Comments ### List Comments for Post ``` GET /api/posts/{id}/comments?sort={sort}&page={page}&limit={limit} ``` **Sort options:** `best` (Wilson score), `new` (newest), `top` (highest score), `controversial`. Comments are threaded with `parentId`. Maximum nesting depth: 10 levels. Materialized path stored for efficient tree queries. **Response:** ```json { "comments": [ { "id": "uuid", "content": "Comment text", "author": { "id": "uuid", "name": "User", "image": "url" }, "parentId": null, "depth": 0, "upvotes": 5, "downvotes": 0, "score": 5, "userVote": 0, "createdAt": "2026-01-01T00:00:00.000Z", "replies": [] } ], "pagination": { "page": 1, "limit": 50, "total": 23 } } ``` Deleted comments show `content: "[deleted]"` and `author: { name: "[deleted]" }` if they have replies (soft delete). Comments without replies are hard-deleted. ### Create Comment (Auth Required) ``` POST /api/posts/{id}/comments Content-Type: application/json { "content": "string (required, max 10000)", "parentId": "uuid (optional, for replies)" } ``` ### Edit Comment (Auth Required, Owner) ``` PUT /api/comments/{id} Content-Type: application/json { "content": "string (required, max 10000)" } ``` ### Delete Comment (Auth Required, Owner/MODERATOR/ADMIN) ``` DELETE /api/comments/{id} ``` --- ## Reactions ### Toggle Reaction (Auth Required) ``` POST /api/reactions Content-Type: application/json { "postId": "uuid", "type": "heart | unicorn | fire | clap" } ``` Toggles: if reaction exists, removes it; if not, creates it. **Response:** ```json { "reactions": { "heart": 5, "unicorn": 2, "fire": 0, "clap": 3 }, "userReactions": ["heart", "clap"] } ``` ### Get Reactions ``` GET /api/reactions?postId={uuid} ``` Returns counts and user's reactions (if authenticated). --- ## Bookmarks ### Add Bookmark (Auth Required) ``` POST /api/bookmarks Content-Type: application/json { "postId": "uuid" } ``` ### Remove Bookmark (Auth Required) ``` DELETE /api/bookmarks Content-Type: application/json { "postId": "uuid" } ``` ### List Bookmarks (Auth Required) ``` GET /api/bookmarks?page={page}&limit={limit} ``` Returns bookmarked posts with full metadata. --- ## AI Chat ### Chat (Auth Recommended) ``` POST /api/ai/chat Content-Type: application/json { "messages": [ { "role": "user", "content": "Tell me about SolanaLink's cloud services" } ], "locale": "ja" } ``` Uses Claude (AWS Bedrock). Responses are cached in Redis. Returns: ```json { "content": "SolanaLink offers comprehensive cloud solutions...", "usage": { "inputTokens": 150, "outputTokens": 300 }, "cached": false } ``` ### Streaming Chat ``` POST /api/ai/stream Content-Type: application/json { "messages": [...], "locale": "ja" } ``` Returns Server-Sent Events (SSE): ``` data: {"text": "SolanaLink"} data: {"text": " offers"} data: {"text": " comprehensive"} data: [DONE] ``` --- ## Contact Form ``` POST /api/contact Content-Type: application/json { "name": "string (required)", "email": "string (required, valid email)", "company": "string (optional)", "phone": "string (optional)", "subject": "string (required, 3-200 chars)", "message": "string (required, 10-5000 chars)", "locale": "ja | en (default: ja)", "recaptchaToken": "string (required in production)" } ``` Anti-spam: honeypot field (`website` must be empty) + reCAPTCHA v3. --- ## Health Check ``` GET /api/health ``` **Response:** ```json { "status": "healthy", "timestamp": "2026-01-01T00:00:00.000Z", "services": { "database": "connected", "redis": "connected" } } ``` --- ## Error Codes All API errors include self-healing metadata for agents: ```json { "error": "Human-readable message", "code": "ERROR_CODE", "doc_url": "https://solanalink.jp/llms-full.txt#error-codes", "is_retriable": false, "alternative_action": "Suggested workaround", "retry_after": 30 } ``` | Code | HTTP | Retriable | Alternative Action | |------|------|-----------|-------------------| | `NOT_FOUND` | 404 | No | Check /api/v1/posts for valid slugs and IDs | | `UNAUTHORIZED` | 401 | No | Authenticate via OAuth or API key | | `FORBIDDEN` | 403 | No | Check required role: AUTHOR, MODERATOR, or ADMIN | | `BAD_REQUEST` | 400 | No | -- | | `RATE_LIMITED` | 429 | Yes | Reduce request frequency or authenticate for higher limits | | `CONFLICT` | 409 | No | Fetch current state before retrying | | `VALIDATION_ERROR` | 422 | No | See fieldErrors for specific field issues | | `INTERNAL_ERROR` | 500 | Yes | Retry after 5 seconds | Validation errors include field-level details: ```json { "error": "Validation failed", "code": "VALIDATION_ERROR", "doc_url": "https://solanalink.jp/llms-full.txt#error-codes", "is_retriable": false, "alternative_action": "See fieldErrors for specific field issues", "fieldErrors": { "title": ["Required"], "content": ["String must contain at least 1 character(s)"] } } ``` --- ## Scoring Algorithms ### Hot Score (Trending) Time-decay algorithm similar to Reddit. Recent posts with high engagement rank higher. Used for default "hot" sort and trending sidebar. ### Wilson Score (Best Comments) Statistical confidence interval for "best" comment sorting. Handles small sample sizes better than simple upvote/downvote ratio. Used for "best" comment sort. ### Score Simple `upvotes - downvotes`. Used for "top" sort order. --- ## Content Model ### Post Statuses | Status | Description | |--------|-------------| | `DRAFT` | Not visible to public | | `PUBLISHED` | Visible, appears in feeds | | `SCHEDULED` | Will auto-publish at `scheduledAt` | | `ARCHIVED` | Hidden from listings | ### Content Formats Posts support `MARKDOWN`, `HTML`, or `RICH_TEXT` content format. Default is `MARKDOWN`. ### Locales All content tagged with locale: `ja` (Japanese, default), `en` (English), `zh` (Chinese). Categories and tags have localized names (`nameJa`, `nameEn`). --- ## Feeds | Feed | URL | Format | Update Interval | |------|-----|--------|----------------| | RSS (ja) | `/ja/feed.xml` | RSS 2.0 | 30 minutes | | RSS (en) | `/en/feed.xml` | RSS 2.0 | 30 minutes | | Atom (ja) | `/ja/atom.xml` | Atom 1.0 | 30 minutes | | Atom (en) | `/en/atom.xml` | Atom 1.0 | 30 minutes | | Sitemap | `/sitemap.xml` | XML | On demand | --- ## Push Notifications ### Web Push (VAPID) ``` POST /api/push/subscribe Content-Type: application/json { "endpoint": "https://...", "keys": { "p256dh": "...", "auth": "..." } } ``` ### Native Device Registration ``` POST /api/push/register-device Content-Type: application/json { "token": "device-push-token", "platform": "ios | android | web" } ``` ### Send Push (ADMIN Only) ``` POST /api/push/send Content-Type: application/json { "title": "string", "body": "string", "url": "string (optional)", "locale": "ja | en (optional)" } ``` --- ## Public API v1 Versioned, read-only endpoints for agent consumption. No authentication required. Rate-limited at 60 requests/minute per IP. All responses include CORS headers and `X-RateLimit-*` headers. ### Response Envelope ```json // List endpoints { "data": [...], "meta": { "page": 1, "pageSize": 20, "total": 150, "hasMore": true } } // Detail endpoints { "data": { ... } } ``` ### Endpoints ``` GET /api/v1/posts?locale=ja&sort=new&page=1&pageSize=20&category=slug&tag=slug&search=term GET /api/v1/posts/{slug} GET /api/v1/posts/{slug}/comments?sort=best&page=1&pageSize=20 GET /api/v1/posts/trending?locale=ja&limit=10 GET /api/v1/categories GET /api/v1/categories/{slug}?locale=ja&page=1&pageSize=20 GET /api/v1/tags GET /api/v1/tags/{slug}?locale=ja&page=1&pageSize=20 ``` ### Query Parameters | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `page` | int | 1 | Page number | | `pageSize` | int | 20 | Items per page (max 100) | | `locale` | string | `ja` | Content locale: `ja`, `en`, `zh` | | `sort` | string | `new` | Sort: `hot`, `new`, `top` (posts); `best`, `new`, `top` (comments) | | `category` | string | -- | Filter by category slug (posts list only) | | `tag` | string | -- | Filter by tag slug (posts list only) | | `search` | string | -- | Search in title and excerpt (posts list only) | --- ## API Key Authentication For programmatic and agent access. Create keys via the management endpoints (session auth required). ### Key Format - Production: `sl_live_{32 random hex chars}` - Development: `sl_test_{32 random hex chars}` Full key is shown **once** at creation. Stored as SHA-256 hash. ### Usage ``` Authorization: Bearer sl_live_abc123... ``` ### Scopes | Scope | Allows | |-------|--------| | `posts:read` | List/view published posts | | `posts:write` | Create/update own posts (AUTHOR+ role) | | `comments:read` | View comments | | `comments:write` | Create comments | | `votes:write` | Vote on posts/comments | | `reactions:write` | Add/remove reactions | | `bookmarks:write` | Manage bookmarks | | `ai:chat` | Use AI chat/streaming endpoints | | `profile:read` | Read own profile | ### Management Endpoints (Session Auth Required) ``` POST /api/v1/auth/api-keys Content-Type: application/json { "name": "My Agent Key", "scopes": ["posts:read", "comments:read", "ai:chat"], "expiresInDays": 90 } ``` Response (201): ```json { "id": "uuid", "name": "My Agent Key", "key": "sl_live_abc123...", "keyPrefix": "sl_live_abc12345", "scopes": ["posts:read", "comments:read", "ai:chat"], "expiresAt": "2026-07-01T00:00:00.000Z", "createdAt": "2026-04-01T00:00:00.000Z" } ``` ``` GET /api/v1/auth/api-keys — List active keys (key value masked) DELETE /api/v1/auth/api-keys/{id} — Revoke a key (soft delete) POST /api/v1/auth/api-keys/{id}/rotate — Rotate a key (zero-downtime) ``` Maximum 10 active keys per user. ### Rotate API Key Generates new key material with same name, scopes, and expiration. Old key stays valid for 1 hour (grace period). ``` POST /api/v1/auth/api-keys/{id}/rotate Authorization: Bearer sl_live_oldkey... ``` Response (201): ```json { "id": "new-uuid", "name": "My Agent Key", "key": "sl_live_newkey...", "keyPrefix": "sl_live_newk", "scopes": ["posts:read", "comments:read", "ai:chat"], "expiresAt": "2026-07-01T00:00:00.000Z", "createdAt": "2026-04-06T00:00:00.000Z", "rotatedFrom": { "id": "old-key-id", "keyPrefix": "sl_live_oldk", "gracePeriodEnds": "2026-04-06T01:00:00.000Z" } } --- ## Pre-Execution Authorization High-risk operations via API key return HTTP 202 with a pending action that must be confirmed within 5 minutes. Session-authenticated (browser) requests bypass this — the user is directly present. ### High-Risk Operations - `post.create`, `post.delete` - `comment.delete` - `account.delete` - `api_key.create`, `api_key.revoke` ### Two-Step Flow **Step 1:** High-risk operation returns 202: ```json { "actionId": "uuid", "action": "post.delete", "confirmUrl": "/api/v1/actions/{id}/confirm", "expiresIn": 300, "expiresAt": "2026-04-01T12:05:00.000Z" } ``` **Step 2:** Confirm within 5 minutes: ``` POST /api/v1/actions/{id}/confirm ``` Response: ```json { "data": { "actionId": "uuid", "action": "post.delete", "status": "confirmed", "payload": { ... }, "confirmedAt": "2026-04-01T12:01:00.000Z" } } ``` **Check status:** ``` GET /api/v1/actions/{id} ``` Returns: `{ "data": { "id", "action", "status", "expiresAt", "confirmedAt", "createdAt" } }` Status values: `pending`, `confirmed`, `expired`. --- ## Rate Limiting All endpoints are rate-limited with Redis-backed fixed-window counters. If Redis is unavailable, requests are allowed (fail-open). ### Response Headers All responses include: - `X-RateLimit-Limit` — Maximum requests in the window - `X-RateLimit-Remaining` — Remaining requests - `X-RateLimit-Reset` — Unix timestamp when the window resets ### Tiers | Tier | Window | Max Requests | Applied To | |------|--------|-------------|-----------| | Public read | 1 min | 60 | `/api/v1/*` unauthenticated | | Authenticated read | 1 min | 120 | Authenticated GET requests | | Authenticated write | 1 min | 30 | POST/PUT/DELETE | | AI chat | 1 min | 10 | `/api/ai/*` | | Contact | 15 min | 5 | `/api/contact` | When rate-limited, the response includes `retry_after` (seconds to wait): ```json { "error": "Too many requests", "code": "RATE_LIMITED", "is_retriable": true, "retry_after": 45, "doc_url": "https://solanalink.jp/llms-full.txt#rate-limiting" } ``` --- ## OpenAPI Specification Machine-readable API contract available at: ``` GET /api/openapi.json ``` OpenAPI 3.1 compliant. 40+ operations across 8 tags. 1-hour cache. CORS enabled. Tags: Public Content, Posts, Comments, Engagement, AI, Auth, Push, System. --- ## Agent Discovery Endpoints | Endpoint | Format | Description | |----------|--------|-------------| | `GET /llms.txt` | Markdown | Capability summary | | `GET /llms-full.txt` | Markdown | Full API reference (this document) | | `GET /api/openapi.json` | JSON | OpenAPI 3.1 specification | | `GET /.well-known/ai-plugin.json` | JSON | OpenAI plugin manifest | | `GET /.well-known/agent.json` | JSON | A2A agent card (4 skills) | | `GET /datasets/solanalink-api-examples.jsonl` | JSONL | 45 instruction-response training examples | | `GET /{locale}/feed.xml` | XML | RSS 2.0 feed | | `GET /{locale}/atom.xml` | XML | Atom 1.0 feed | | `GET /sitemap.xml` | XML | Sitemap with locale alternates |