Files
jarvis/API/contract.md
Robin Kutesa 27d0e22767 Initial commit: Jarvis iOS app
SwiftUI client for a self-hosted RSS news-correlation platform: signal feed,
story detail, article reader, feed manager, and LAN⇄Tailscale connectivity.
Project generated from project.yml via XcodeGen.

Includes CI build matrix (macOS 14/15 × Debug/Release), issue templates,
backlog, and API/backend handoff docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 18:04:59 +03:00

6.0 KiB

Jarvis API Contract

Base URL: http://<host>:<port>/api/v1


Authentication

None. Local network / self-hosted only. All endpoints are open. Secure at the network level (VPN, firewall).


REST Endpoints

GET /stories

Returns paginated stories sorted by signal score descending.

Query params

Param Type Default Description
limit int 20 Max stories per page (max 100)
after string Cursor for next page
topic string Filter by topic slug e.g. finance, tech
min_signal int Only return stories above this score

Response 200

{
  "data": [
    {
      "id": "story_2847",
      "headline": "Bank of Uganda cuts interest rates",
      "summary": "The Bank of Uganda reduced its benchmark lending rate by 50 basis points, citing easing inflation and the need to stimulate credit growth.",
      "topic": "finance",
      "signalScore": 91,
      "scoreBreakdown": {
        "sourceAuthority": 28,
        "freshness": 22,
        "localRelevance": 15,
        "crossSourceConfirmation": 16,
        "topicImportance": 10
      },
      "sourceCount": 7,
      "sources": [
        {
          "id": "src_001",
          "name": "Daily Monitor",
          "url": "https://monitor.co.ug",
          "publishedAt": "2026-06-18T08:03:00Z",
          "isBreaking": true
        }
      ],
      "consensus": "All 7 sources confirm the rate cut. Daily Monitor and NilePost agree on the 50bp figure.",
      "conflict": null,
      "cachedAt": null,
      "updatedAt": "2026-06-18T09:45:00Z",
      "createdAt": "2026-06-18T08:03:00Z"
    }
  ],
  "nextCursor": "cursor_abc123",
  "hasMore": true,
  "total": 248
}

GET /stories/:id

Returns full story detail including article timeline.

Response 200

{
  "id": "story_2847",
  "headline": "Bank of Uganda cuts interest rates",
  "summary": "...",
  "topic": "finance",
  "signalScore": 91,
  "scoreBreakdown": {
    "sourceAuthority": 28,
    "freshness": 22,
    "localRelevance": 15,
    "crossSourceConfirmation": 16,
    "topicImportance": 10
  },
  "sourceCount": 7,
  "consensus": "All 7 sources confirm the rate cut.",
  "conflict": "Two regional outlets dispute impact on mortgage rates.",
  "timeline": [
    {
      "articleId": "art_001",
      "source": "Daily Monitor",
      "headline": "BoU cuts lending rate by 50 basis points",
      "publishedAt": "2026-06-18T08:03:00Z",
      "isBreaking": true
    },
    {
      "articleId": "art_002",
      "source": "NilePost",
      "headline": "Central bank eases policy amid inflation slowdown",
      "publishedAt": "2026-06-18T08:21:00Z",
      "isBreaking": false
    }
  ],
  "updatedAt": "2026-06-18T09:45:00Z",
  "createdAt": "2026-06-18T08:03:00Z"
}

GET /articles/:id

Returns full article content for offline caching.

Response 200

{
  "id": "art_001",
  "storyId": "story_2847",
  "source": "Daily Monitor",
  "sourceUrl": "https://monitor.co.ug",
  "headline": "BoU cuts lending rate by 50 basis points",
  "body": "Full article text...",
  "imageUrl": "https://...",
  "author": "John Doe",
  "publishedAt": "2026-06-18T08:03:00Z"
}

GET /feeds

Returns all configured RSS feeds with health state.

Response 200

{
  "data": [
    {
      "id": "feed_001",
      "name": "Daily Monitor",
      "url": "https://monitor.co.ug/rss",
      "health": "active",
      "pollIntervalSeconds": 300,
      "failureCount": 0,
      "lastFetchedAt": "2026-06-18T09:40:00Z",
      "articleCountToday": 44
    }
  ]
}

POST /feeds

Add a new RSS feed.

Request

{
  "url": "https://example.com/rss",
  "name": "Example News",
  "pollIntervalSeconds": 900
}

Response 201

Returns the created feed object.


DELETE /feeds/:id

Remove a feed.

Response 204 No content.


GET /health

Server health check. Used by the app on launch to verify connection.

Response 200

{
  "status": "ok",
  "version": "1.0.0",
  "storiesCount": 248,
  "feedsCount": 37,
  "uptime": 86400
}

WebSocket

Connect

ws://<host>:<port>/ws

No auth headers required.


Events (server → client)

story.updated

Signal score changed or new article joined the story.

{
  "type": "story.updated",
  "storyId": "story_2847",
  "signalScore": 94,
  "sourceCount": 9,
  "updatedAt": "2026-06-18T10:01:00Z"
}

App behaviour: refetch GET /stories/story_2847, update local cache.


story.created

New story formed from a new cluster.

{
  "type": "story.created",
  "storyId": "story_2901",
  "headline": "Parliament passes new budget",
  "signalScore": 42,
  "topic": "politics",
  "createdAt": "2026-06-18T10:05:00Z"
}

App behaviour: insert into feed, resort by signal score.


story.stale

Story has gone cold — no new articles in decay window.

{
  "type": "story.stale",
  "storyId": "story_2800",
  "updatedAt": "2026-06-18T10:10:00Z"
}

App behaviour: dim story in feed or remove based on user preference.


feed.health

A feed's health state changed.

{
  "type": "feed.health",
  "feedId": "feed_012",
  "health": "failing",
  "failureCount": 3,
  "updatedAt": "2026-06-18T10:12:00Z"
}

App behaviour: update feed health indicator in feed manager.


Ping / Pong

Server sends {"type":"ping"} every 30s. Client must respond with {"type":"pong"}. No response in 60s → server closes connection. Client should reconnect with exponential backoff: 1s → 2s → 4s → 8s → max 60s.


Error shapes

All errors follow this shape:

{
  "error": {
    "code": "story_not_found",
    "message": "No story with id story_9999",
    "status": 404
  }
}
Code Status Meaning
story_not_found 404 Story ID does not exist
feed_not_found 404 Feed ID does not exist
invalid_cursor 400 Pagination cursor is malformed
invalid_url 400 Feed URL is not a valid RSS feed
server_error 500 Internal server error