Community News API Reference
The Community News API lets you access community journalism content programmatically. Build integrations, let AI agents read your communities, or automate workflows.
Machine-readable discovery:
- /api/v1 — JSON discovery endpoint (no auth required)
- /llms.txt — plain-text quick reference for LLMs
- /.well-known/agent-card.json — structured capability manifest
Authentication
All authenticated endpoints require a Bearer token in the Authorization header:
Authorization: Bearer cn_your_api_key_here
Creating API Keys
- Log in to your account
- Go to Profile Settings → API Keys
- Click Create API Key
- Copy the key immediately — it's shown only once
Key Scoping
API keys inherit your role (admin or member) in each community. Keys can be:
- All communities — access every community you're a member of
- Scoped — restricted to specific communities you choose
Base URL
https://alaskanews.news/api/v1
For local development: http://localhost:3200/api/v1
Response Format
Success
{
"data": { ... },
"next_steps": [
{ "rel": "articles", "method": "GET", "href": "/api/v1/communities/slug/articles", "description": "Browse articles" }
]
}
List (paginated)
{
"data": [ ... ],
"count": 5,
"offset": 0,
"limit": 20,
"has_more": false,
"next_steps": [ ... ]
}
Error
{
"error": "not_found",
"message": "Article not found: abc123",
"suggestion": "Check the article ID and try again."
}
HTTP Status Codes
| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 401 | Unauthorized — missing or invalid API key |
| 403 | Forbidden — key doesn't have access to this resource |
| 404 | Not found |
| 422 | Validation error — check the issues field |
| 429 | Rate limited — check Retry-After header |
| 500 | Server error |
HATEOAS: next_steps
Every response includes a next_steps array with suggested actions. Each step has:
rel— relationship type (e.g., "articles", "community")method— HTTP method (GET, POST, PUT)href— the endpoint URLdescription— what this action does
Follow next_steps to navigate the API without memorizing endpoints.
Rate Limits
| Type | Limit |
|---|---|
| Read (GET) | 100 requests per minute |
| Write (POST/PUT) | 20 requests per minute |
Rate limits are per API key. When exceeded, you'll receive a 429 response with a Retry-After header.
Endpoints
Discovery
GET /api/v1
Returns API metadata, available endpoints, and documentation links. No authentication required.
curl https://alaskanews.news/api/v1
Articles
GET /api/v1/articles
List published articles for a community.
Query Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
| community | string | Yes* | Community slug (*optional for foryou sort) |
| limit | number | No | Max results (default: 20, max: 100) |
| offset | number | No | Skip first N results (default: 0) |
| sort | string | No | Sort mode: hot (default), new, nearby, top, rising, controversial, foryou |
| t | string | No | Time window for top sort: today, week (default), month, all |
| lat | number | No | Latitude for nearby sort |
| lng | number | No | Longitude for nearby sort |
| r | number | No | Radius in miles for nearby sort (default: 50, min: 5, max: 500) |
# Default (hot) sort
curl -H "Authorization: Bearer cn_..." \
"https://alaskanews.news/api/v1/articles?community=municipality-of-anchorage&limit=10"
# Top articles this month
curl -H "Authorization: Bearer cn_..." \
"https://alaskanews.news/api/v1/articles?community=municipality-of-anchorage&sort=top&t=month"
# Nearby articles within 100 miles
curl -H "Authorization: Bearer cn_..." \
"https://alaskanews.news/api/v1/articles?community=municipality-of-anchorage&sort=nearby&lat=61.2&lng=-149.9&r=100"
# Rising articles (gaining traction recently)
curl -H "Authorization: Bearer cn_..." \
"https://alaskanews.news/api/v1/articles?community=municipality-of-anchorage&sort=rising"
# Controversial articles (high engagement, mixed reactions)
curl -H "Authorization: Bearer cn_..." \
"https://alaskanews.news/api/v1/articles?community=municipality-of-anchorage&sort=controversial"
# Personalized feed (excludes already-read articles)
curl -H "Authorization: Bearer cn_..." \
"https://alaskanews.news/api/v1/articles?sort=foryou"
POST /api/v1/articles
Create a new draft article.
Request Body:
{
"community_id": "uuid",
"title": "Article Title",
"slug": "article-title",
"content": { "type": "doc", "content": [] },
"excerpt": "Optional excerpt"
}
GET /api/v1/articles/:id
Get a single article by ID.
curl -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/articles/uuid
PUT /api/v1/articles/:id
Update a draft article. Same body format as POST.
POST /api/v1/articles/:id/submit
Submit a draft for peer review. No request body needed.
Communities
GET /api/v1/communities
List communities accessible to your API key. Supports sorting.
Query Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
| limit | number | No | Max results (default: 20, max: 100) |
| offset | number | No | Skip first N results (default: 0) |
| sort | string | No | Sort mode: hot (default), new, nearby, top, rising, controversial, foryou |
| t | string | No | Time window for top sort: today, week (default), month, all |
| lat | number | No | Latitude for nearby sort |
| lng | number | No | Longitude for nearby sort |
| r | number | No | Radius in miles for nearby sort (default: 50, min: 5, max: 500) |
curl -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/communities
# Communities near Anchorage
curl -H "Authorization: Bearer cn_..." \
"https://alaskanews.news/api/v1/communities?sort=nearby&lat=61.2&lng=-149.9"
GET /api/v1/communities/:slug
Get community details by slug.
curl -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/communities/municipality-of-anchorage
GET /api/v1/communities/:slug/articles
List published articles in a community. Supports sorting.
| Param | Type | Required | Description |
|---|---|---|---|
| limit | number | No | Max results (default: 20, max: 100) |
| offset | number | No | Skip first N results (default: 0) |
| sort | string | No | Sort mode: hot (default), new, nearby, top, rising, controversial, foryou |
| t | string | No | Time window for top sort: today, week (default), month, all |
| lat | number | No | Latitude for nearby sort |
| lng | number | No | Longitude for nearby sort |
| r | number | No | Radius in miles for nearby sort (default: 50, min: 5, max: 500) |
GET /api/v1/communities/:slug/members
List members of a community.
Memberships
GET /api/v1/memberships
List your community memberships and roles.
curl -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/memberships
POST /api/v1/memberships
Join a community.
{
"community_slug": "fairbanks-community-news"
}
Search
GET /api/v1/search
Full-text search across articles, communities, and users.
| Param | Type | Required | Description |
|---|---|---|---|
| q | string | Yes | Search query |
| type | string | No | Filter: "all", "articles", or "communities" (default: "all") |
| limit | number | No | Max results (default: 20, max: 100) |
curl -H "Authorization: Bearer cn_..." \
"https://alaskanews.news/api/v1/search?q=budget&type=articles"
Reviews
GET /api/v1/articles/:id/reviews
List reviews for an article.
POST /api/v1/articles/:id/reviews
Submit a review.
{
"decision": "approve",
"comment": "Optional review comment"
}
Valid decisions: approve, request_changes, reject. If enough approvals are reached, the article is auto-published.
Comments
GET /api/v1/articles/:id/comments
List comments on an article.
POST /api/v1/articles/:id/comments
Add a comment.
{
"content": "Great article!"
}
PUT /api/v1/articles/:id/comments/:commentId
Edit a comment (within 1 hour of creation).
DELETE /api/v1/articles/:id/comments/:commentId
Delete your own comment.
Reactions
GET /api/v1/articles/:id/reactions
Get reaction counts and your reactions for an article.
POST /api/v1/articles/:id/reactions
Toggle a reaction (add if not exists, remove if exists).
{
"emoji": "👍"
}
Valid emojis: 👍 👎 ❤️ 🔥 👀 😂 🎯 💯
Bookmarks
POST /api/v1/articles/:id/bookmarks
Toggle bookmark on an article (add if not bookmarked, remove if bookmarked).
Topics
GET /api/v1/topics
List all topics with article stats. Supports sorting.
Query Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
| limit | number | No | Max results (default: 20, max: 100) |
| offset | number | No | Skip first N results (default: 0) |
| sort | string | No | Sort mode: hot (default), new, top |
| t | string | No | Time window for top sort: today, week (default), month, all |
curl -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/topics
GET /api/v1/topics/:slug
Get topic details and published articles for a topic.
Query Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
| limit | number | No | Max results (default: 20, max: 100) |
| offset | number | No | Skip first N results (default: 0) |
| sort | string | No | Sort mode: hot (default), new, top |
| t | string | No | Time window for top sort |
curl -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/topics/government
Co-authors
GET /api/v1/articles/:id/coauthors
List co-authors for an article.
curl -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/articles/uuid/coauthors
POST /api/v1/articles/:id/coauthors
Invite a co-author to an article.
{
"user_id": "uuid"
}
PUT /api/v1/articles/:id/coauthors/:coauthorId
Accept or decline a co-author invitation. Only the invited user can respond.
{
"status": "accepted"
}
Valid statuses: accepted, declined.
Content Sources
Content sources represent all types of input content (video, audio, email, web scrape, RSS, PDF, manual text) that can be processed through the pipeline into articles.
GET /api/v1/communities/:slug/sources
List content sources for a community.
Query Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
| type | string | No | Filter by source type: video, audio, email, web_scrape, rss, pdf, manual |
| status | string | No | Filter by status: pending, downloading, processing, transcribing, classifying, classified, drafting, drafted, skipped, failed, completed |
| limit | number | No | Max results (default: 20, max: 100) |
| offset | number | No | Skip first N results (default: 0) |
# List all sources
curl -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/communities/municipality-of-anchorage/sources
# Filter by type
curl -H "Authorization: Bearer cn_..." \
"https://alaskanews.news/api/v1/communities/municipality-of-anchorage/sources?type=email&status=classified"
POST /api/v1/communities/:slug/sources
Create a new content source.
Request Body:
{
"source_type": "manual",
"title": "City Council Meeting Notes",
"source_url": "https://example.com/meeting",
"body_text": "The council voted 7-4 to approve...",
"author_name": "Jane Reporter"
}
| Field | Type | Required | Description |
|---|---|---|---|
| source_type | string | Yes | One of: video, audio, email, web_scrape, rss, pdf, manual |
| title | string | No | Source title or headline |
| source_url | string | No | URL of the original content |
| body_text | string | No | Plain text content |
| body_html | string | No | HTML content (for emails or web scrapes) |
| author_name | string | No | Original author or sender |
GET /api/v1/communities/:slug/sources/:id
Get source details including transcript chunks and linked drafted articles.
curl -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/communities/municipality-of-anchorage/sources/uuid
Response includes: Source metadata, transcript_chunks array (timestamped text segments), and drafted_articles array (articles generated from this source).
POST /api/v1/communities/:slug/sources/:id/classify
Trigger LLM classification for a content source. Uses Claude to analyze the source content and extract structured metadata including category, topics, entities, priority, and newsworthiness assessment.
curl -X POST -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/communities/municipality-of-anchorage/sources/uuid/classify
Response: Returns the classification result with category, topics, priority, is_newsworthy, entities, summary, and suggested_headline.
POST /api/v1/communities/:slug/sources/:id/draft
Trigger article drafting from a content source. Creates a pipeline run for the drafting stage.
curl -X POST -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/communities/municipality-of-anchorage/sources/uuid/draft
Response (201 Created):
{
"data": {
"run_id": "uuid",
"source_id": "uuid",
"stage": "drafting",
"status": "pending"
}
}
User Profile
GET /api/v1/me
Get the authenticated user's profile, including memberships with roles and community names.
curl -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/me
Response includes: id, display_name, bio, avatar_url, location, latitude, longitude, news_radius_miles, timezone, website_url, social_links, is_public, created_at, and memberships array (each with role, reputation_score, is_muted, and nested communities object).
PUT /api/v1/me
Update the authenticated user's profile.
Request Body (all fields optional):
{
"display_name": "Jane Reporter",
"bio": "Local journalist covering city council",
"location": "Anchorage, AK",
"latitude": 61.2181,
"longitude": -149.9003,
"news_radius_miles": 50,
"timezone": "America/Anchorage",
"website_url": "https://example.com",
"social_links": { "twitter": "@jane" },
"is_public": true
}
At least one field must be provided.
User Bookmarks
GET /api/v1/me/bookmarks
List the authenticated user's bookmarked articles with community info.
Query Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
| limit | number | No | Max results (default: 20, max: 100) |
| offset | number | No | Skip first N results (default: 0) |
curl -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/me/bookmarks
Each item includes article details (id, title, slug, excerpt, published_at, engagement counts) plus bookmarked_at timestamp and nested communities info.
API Keys
POST /api/v1/api-keys
Create a new API key programmatically. The full key is returned only once at creation time.
Request Body:
{
"name": "My Integration",
"community_ids": ["uuid1", "uuid2"],
"expires_at": "2027-01-01T00:00:00Z"
}
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Descriptive name for the key |
| community_ids | string[] | No | Scope to specific communities (default: all) |
| expires_at | string | No | ISO 8601 expiration date (default: never) |
curl -X POST -H "Authorization: Bearer cn_..." \
-H "Content-Type: application/json" \
-d '{"name": "CI Pipeline"}' \
https://alaskanews.news/api/v1/api-keys
Response (201 Created):
{
"data": {
"key": "cn_a1b2c3...",
"prefix": "cn_a1b2",
"name": "CI Pipeline"
}
}
Pagination
All list endpoints support pagination with offset and limit query parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| limit | number | 20 | Results per page (max: 100) |
| offset | number | 0 | Number of results to skip |
Response includes has_more: true when more results are available.
Notifications
List Notifications
GET /api/v1/notifications?limit=20&offset=0&unread=true
Returns paginated notifications for the authenticated user. Use unread=true to filter to unread only.
Mark Notification as Read
PATCH /api/v1/notifications/:id
Marks a single notification as read. Send a JSON body with { "is_read": true }.
Mark All as Read
POST /api/v1/notifications/mark-all-read
Marks all unread notifications as read for the authenticated user.
Content Reporting
Report an Article
POST /api/v1/articles/:id/report
Report an article for violating community guidelines.
{
"reason": "misinformation",
"description": "Optional details about the report"
}
Valid reasons: spam, harassment, misinformation, off_topic, hate_speech, other.
Report a Comment
POST /api/v1/articles/:id/comments/:commentId/report
Report a comment. Same request body format as article reports.
Moderation (Admin Only)
List Content Reports
GET /api/v1/communities/:slug/moderation/reports?status=pending&limit=20&offset=0
List content reports for a community. Requires admin role. Supports status filter (pending, reviewed, action_taken, dismissed) and pagination.
Resolve a Report
PUT /api/v1/communities/:slug/moderation/reports/:id
Resolve a content report.
{
"status": "dismissed",
"note": "Optional resolution note"
}
Valid statuses: reviewed, action_taken, dismissed.
Voice Profiles
Journalist voice profiles capture writing style for AI-powered drafting and refinement. Profiles are versioned so voice evolution can be tracked over time.
Get Voice Profile
GET /api/v1/voice-profile
Returns the authenticated user's active voice profile and full version history.
Response:
{
"data": {
"active": {
"id": "uuid",
"user_id": "uuid",
"version": 2,
"voice_summary": "Lead with the key decision and vote count...",
"dimensions": {
"avg_sentence_length": 18,
"vocabulary_grade": 8,
"formality": 4,
"person": "third",
"uses_contractions": false,
"tone": "neutral and informative",
"lead_style": "decision-first",
"paragraph_style": "short punchy",
"closing_pattern": "call-to-action",
"closest_style": "ap_wire",
"signature_patterns": ["opens with specific vote counts", "..."],
"never_do": ["Never use em dashes", "Never use rhetorical questions"],
"source": "manual"
},
"is_active": true,
"is_public": true,
"created_at": "2026-03-27T..."
},
"history": [
{ "id": "...", "version": 2, "..." : "..." },
{ "id": "...", "version": 1, "..." : "..." }
]
}
}
Calibrate Voice Profile
POST /api/v1/voice-profile
Analyze writing samples to create a new voice profile version. If a current profile exists, it's used as context so the AI builds on previous calibrations and manual edits.
Request body:
{
"sample_texts": [
"The Anchorage Assembly voted 7-4 Tuesday night...",
"A second writing sample..."
]
}
- 1-5 samples, minimum 200 characters total, maximum 50,000 characters total
- Each sample is truncated to 10,000 characters
Response: Returns the newly created voice profile version (201 Created).
Update or Activate Voice Profile
PUT /api/v1/voice-profile/:id
Either activate a previous version or manually update the voice profile.
Activate a version:
{
"action": "activate"
}
Manual update (creates a new version tagged "manual"):
{
"voice_summary": "Lead with the key decision...",
"dimensions": {
"tone": "warm and factual",
"formality": 3,
"never_do": ["Never use em dashes", "Never use passive voice"]
}
}
Dimensions are merged with the current profile -- only the fields you provide are overridden.
Delete Voice Profile Version
DELETE /api/v1/voice-profile/:id
Delete a non-active voice profile version. The active version cannot be deleted -- activate a different version first.
Response:
{
"data": { "deleted": true }
}
Voice Profile Integration
When a voice profile is active, it's automatically injected into:
- Article refinement -- the "Apply Standards" / refine feature uses your voice
- Pipeline drafting -- automated article generation from meeting transcripts matches your voice
- Article tracking -- articles created with a voice profile store the
voice_profile_idfor attribution
API Key Management
Manage your API keys at /profile/settings. You can:
- Create keys with a descriptive name
- Scope keys to specific communities
- Set expiration (30 days, 90 days, 6 months, 1 year, or never)
- Revoke keys that are no longer needed
Keys are hashed and stored securely. The full key is shown only once at creation.
Citizen Worker API
Citizen Workers are volunteers who process transcription jobs using their residential internet connections. These endpoints are used by the Worker App.
Worker Profile
GET /api/v1/worker/profile
Get the authenticated worker's profile and statistics.
curl -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/worker/profile
Response:
{
"data": {
"id": "uuid",
"user_id": "uuid",
"display_name": "Jane Worker",
"status": "approved",
"trust_level": "trusted",
"quality_score": 92,
"jobs_completed": 25,
"jobs_failed": 1,
"last_active_at": "2026-03-27T...",
"communities": [
{ "id": "uuid", "name": "Municipality of Anchorage", "slug": "municipality-of-anchorage" }
]
}
}
Job Queue
GET /api/v1/worker/jobs/available
Get the next available job for the authenticated worker.
curl -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/worker/jobs/available
Response: Returns a job object if one is available, or { "data": null } if no jobs are pending.
POST /api/v1/worker/jobs/:id/claim
Claim a specific job. Jobs are atomically claimed to prevent race conditions.
curl -X POST -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/worker/jobs/uuid/claim
Response (200):
{
"data": {
"id": "uuid",
"url": "https://youtube.com/watch?v=...",
"platform": "youtube",
"job_type": "vod",
"status": "processing",
"claimed_at": "2026-03-27T..."
}
}
Job Processing
POST /api/v1/worker/jobs/:id/heartbeat
Send a heartbeat to indicate the job is still being processed. Workers should send heartbeats every 30 seconds.
curl -X POST -H "Authorization: Bearer cn_..." \
-H "Content-Type: application/json" \
-d '{"progress": 0.5, "message": "Processing chunk 3 of 6"}' \
https://alaskanews.news/api/v1/worker/jobs/uuid/heartbeat
Response:
{
"data": {
"ok": true,
"cancelled": false,
"cancellation_message": null
}
}
If cancelled: true, the worker should stop processing immediately.
POST /api/v1/worker/jobs/:id/chunks
Upload an audio chunk for transcription.
curl -X POST -H "Authorization: Bearer cn_..." \
-F "file=@chunk_0.mp3" \
-F "chunk_index=0" \
-F "start_offset=0" \
-F "end_offset=300" \
https://alaskanews.news/api/v1/worker/jobs/uuid/chunks
| Field | Type | Required | Description |
|---|---|---|---|
| file | file | Yes | MP3 audio file (max 100MB) |
| chunk_index | number | Yes | Zero-based chunk index |
| start_offset | number | No | Start time in seconds |
| end_offset | number | No | End time in seconds |
POST /api/v1/worker/jobs/:id/complete
Mark a job as completed.
curl -X POST -H "Authorization: Bearer cn_..." \
-H "Content-Type: application/json" \
-d '{"chunks_uploaded": 6}' \
https://alaskanews.news/api/v1/worker/jobs/uuid/complete
POST /api/v1/worker/jobs/:id/fail
Report a job failure.
curl -X POST -H "Authorization: Bearer cn_..." \
-H "Content-Type: application/json" \
-d '{"error": "Download failed: video unavailable"}' \
https://alaskanews.news/api/v1/worker/jobs/uuid/fail
Livestream Jobs
GET /api/v1/worker/jobs/:id/live-status
Get the current status of a livestream job.
curl -H "Authorization: Bearer cn_..." \
https://alaskanews.news/api/v1/worker/jobs/uuid/live-status
POST /api/v1/worker/jobs/:id/transcript
Submit a partial transcript for a livestream job.
curl -X POST -H "Authorization: Bearer cn_..." \
-H "Content-Type: application/json" \
-d '{"text": "The meeting will now come to order...", "timestamp": 120}' \
https://alaskanews.news/api/v1/worker/jobs/uuid/transcript
Admin: Worker Management
These endpoints require platform admin access.
List Workers
GET /api/v1/workers?status=approved&trust_level=trusted&limit=20&offset=0
Query Parameters:
| Param | Type | Description |
|---|---|---|
| status | string | Filter by status: pending, approved, suspended |
| trust_level | string | Filter by trust level: new, trusted, verified |
| limit | number | Max results (default: 20) |
| offset | number | Skip first N results |
Get Worker Details
GET /api/v1/workers/:id
Returns worker profile, communities, recent jobs, and spot check history.
Approve Worker
POST /api/v1/workers/:id/approve
Approve a pending worker application.
Suspend Worker
POST /api/v1/workers/:id/suspend
{
"reason": "Quality issues - multiple failed spot checks"
}
Reinstate Worker
POST /api/v1/workers/:id/reinstate
Reinstate a suspended worker.
Admin: Job Management
These endpoints require platform admin access.
List Jobs
GET /api/v1/jobs?status=processing&platform=youtube&limit=20&offset=0
Query Parameters:
| Param | Type | Description |
|---|---|---|
| status | string | Filter by status: pending, processing, completed, failed, cancelled |
| platform | string | Filter by platform: youtube, vimeo, facebook, etc. |
| job_type | string | Filter by type: vod, livestream |
| worker_id | string | Filter by assigned worker |
| limit | number | Max results (default: 20) |
| offset | number | Skip first N results |
Create Job
POST /api/v1/jobs
{
"community_id": "uuid",
"url": "https://youtube.com/watch?v=...",
"priority": 5,
"job_type": "vod"
}
Get Job Details
GET /api/v1/jobs/:id
Returns job details including chunks and transcription status.
Update Job Priority
PATCH /api/v1/jobs/:id
{
"priority": 10
}
Cancel Job
POST /api/v1/jobs/:id/cancel
{
"message": "Video is no longer available"
}
Retry Failed Job
POST /api/v1/jobs/:id/retry
Force retry a failed job by returning it to the queue.