# LinxTime Agent Protocol — SKILL.md

> Version: 0.1 | Updated: 2026-03-14 | Status: Production-ready
> Docs: https://linxtime.com/docs/agent-protocol
> API Base: https://api.linxtime.com/v1/agent

---

## What is LinxTime?

LinxTime is a group scheduling platform where participants vote on options across multiple aspects (date/time, location, budget, activity) to find consensus. It supports three event types:

- **POLL** — Multi-option voting with quorum mechanics. Participants vote on option bundles, organizer locks the winner.
- **SIGNUP** — Capacity-limited sessions. Participants register for available slots.
- **BOOKING** — 1:1 appointment scheduling. One slot per booking with optional host approval.

LinxTime acts as a **mediator** for multi-agent schedule negotiation — agents never communicate directly with each other.

---

## What Can Agents Do?

AI agents interact with LinxTime as first-class participants with the same capabilities and constraints as human users:

- **Vote** on proposed date/time, location, budget, and activity options
- **Suggest** new options on existing polls
- **Create** events (polls, signups, booking pages)
- **Invite** participants via email or phone
- **Comment** on events
- **Register** for signup sessions
- **Book** appointment slots
- **Negotiate** schedules with other agents (LinxTime mediates)
- **Receive** real-time notifications via SSE event stream

All actions are scoped to permissions the user explicitly grants.

---

## VotableAspect Model

Every option in LinxTime can carry values for one or more **aspects**. The platform supports six aspect types:

### `DATE_TIME`

When the event happens.

```json
{
  "aspect_type": "DATE_TIME",
  "data": {
    "startDate": "2026-03-20",
    "endDate": "2026-03-20",
    "startTime": "18:00",
    "endTime": "22:00"
  }
}
```

### `LOCATION`

Where the event happens.

```json
{
  "aspect_type": "LOCATION",
  "data": {
    "name": "The Pub",
    "address": "Storgatan 12, Stockholm",
    "placeId": "ChIJxyz...",
    "lat": 59.3293,
    "lng": 18.0686
  }
}
```

### `BUDGET`

Cost range for the event.

```json
{
  "aspect_type": "BUDGET",
  "data": {
    "min": 200,
    "max": 400,
    "currency": "SEK"
  }
}
```

### `ACTIVITY`

What the event involves.

```json
{
  "aspect_type": "ACTIVITY",
  "data": {
    "title": "Bowling",
    "description": "Cosmic bowling night with pizza",
    "category": "entertainment"
  }
}
```

### `RECURRENCE_PATTERN`

Recurring event pattern (iCal RRULE).

```json
{
  "aspect_type": "RECURRENCE_PATTERN",
  "data": {
    "rrule": "FREQ=WEEKLY;BYDAY=FR;COUNT=10",
    "description": "Every Friday for 10 weeks",
    "previewDates": ["2026-03-20", "2026-03-27", "2026-04-03"]
  }
}
```

### `SESSION`

Session slot for signup events with capacity tracking.

```json
{
  "aspect_type": "SESSION",
  "data": {
    "title": "Morning Workshop",
    "capacity": 10,
    "startTime": "09:00",
    "endTime": "12:00",
    "duration": 180,
    "location": {
      "name": "Room A",
      "address": "Kungsgatan 8, Stockholm"
    }
  }
}
```

---

## Getting Started

### Install the CLI

```bash
# npm (recommended)
npm install -g @linxtime/cli

# homebrew (macOS/Linux)
brew install linxtime/tap/linxtime
```

### Authenticate

```bash
# Opens browser for OAuth2 flow, stores token locally
linxtime auth login

# Check auth status
linxtime auth status

# Named profile (for multiple accounts)
linxtime auth login --profile work
linxtime auth list
```

Credentials are stored per profile at `~/.linxtime/profiles/<profile>/credentials.json`.

### Start the Daemon

The daemon maintains a persistent SSE connection for real-time notifications:

```bash
# Start background daemon
linxtime daemon start

# Check connection status
linxtime daemon status

# Start daemon for a specific profile
linxtime daemon start --profile work

# Stop daemon
linxtime daemon stop
```

The daemon stores events in a local SQLite inbox at `~/.linxtime/profiles/<profile>/inbox.db` and listens on a Unix socket at `~/.linxtime/profiles/<profile>/daemon.sock`. Multiple daemons can run simultaneously for different profiles.

### MCP Server Mode

Expose LinxTime as an MCP server for agent frameworks (Claude, OpenClaw, Cursor, etc.):

```bash
# Start MCP server on stdio
linxtime mcp serve

# For a specific profile
linxtime mcp serve --profile work
```

Add to your agent's MCP config:

```json
{
  "mcpServers": {
    "linxtime": {
      "command": "linxtime",
      "args": ["mcp", "serve"]
    }
  }
}
```

---

## Authentication

### Token Format

Agent tokens follow the format `lt_agent_<base64>` and are scoped to a specific user and permission set.

### Scopes

| Scope | Allows |
|---|---|
| `links:read` | View links, options, votes the user has access to |
| `links:vote` | Cast/remove votes on options |
| `links:suggest` | Suggest new options on links |
| `links:create` | Create new links (events) |
| `links:manage` | Edit links, invite participants, lock options |
| `notifications:read` | Receive and read notifications |
| `calendar:read` | Read user's calendar availability |
| `integrations:manage` | Initiate/disconnect calendar + payment provider connections |

### Request Access

To use the agent protocol, users create tokens from the LinxTime web UI:

```
Settings → Agent Access → "Create Agent Token" → Select scopes → Copy token
```

Or via CLI after initial OAuth2 login:

```bash
linxtime auth login
# Browser opens → user authenticates → token stored locally
```

### Making Authenticated Requests

```http
POST /v1/agent/act HTTP/1.1
Host: api.linxtime.com
Authorization: Bearer lt_agent_<token>
Content-Type: application/json
```

---

## Action Reference

All write actions go through a single endpoint with an `action` discriminator:

```
POST https://api.linxtime.com/v1/agent/act
Authorization: Bearer lt_agent_<token>
Content-Type: application/json
```

Read endpoints:

```
GET /v1/agent/links              — List user's links
GET /v1/agent/links/:id          — Full link details
GET /v1/agent/links/:id/options  — Options with vote counts
GET /v1/agent/inbox              — Unacknowledged events
```

### Uniform Response Format

**Success:**
```json
{
  "ok": true,
  "action": "option.vote",
  "result": { ... }
}
```

**Error:**
```json
{
  "ok": false,
  "error": "QUOTA_EXCEEDED",
  "message": "Option limit reached for this link",
  "details": { "required_plan": "PRO", "feature": "optionsPerLinx" }
}
```

---

### 1. Vote on an Option

**Scope:** `links:vote`

```json
// Request
{
  "action": "option.vote",
  "link_id": "link_001",
  "option_id": "opt_001"
}

// Response
{
  "ok": true,
  "action": "option.vote",
  "result": {
    "my_votes": ["opt_001"],
    "vote_counts": { "opt_001": 4, "opt_002": 1 }
  }
}
```

### 2. Remove a Vote

**Scope:** `links:vote`

```json
// Request
{
  "action": "option.unvote",
  "link_id": "link_001",
  "option_id": "opt_001"
}

// Response
{
  "ok": true,
  "action": "option.unvote",
  "result": {
    "my_votes": [],
    "vote_counts": { "opt_001": 3, "opt_002": 1 }
  }
}
```

### 3. Suggest a New Option

**Scope:** `links:suggest`

```json
// Request
{
  "action": "option.suggest",
  "link_id": "link_001",
  "label": "Sunday brunch instead",
  "aspect_values": [
    {
      "aspect_type": "DATE_TIME",
      "data": { "startDate": "2026-03-22", "startTime": "11:00", "endTime": "14:00" }
    },
    {
      "aspect_type": "LOCATION",
      "data": { "name": "Café Norden", "address": "Kungsgatan 8, Stockholm" }
    }
  ]
}

// Response
{
  "ok": true,
  "action": "option.suggest",
  "result": {
    "option_id": "opt_003",
    "label": "Sunday brunch instead",
    "vote_count": 0
  }
}
```

### RSVP (Attendance Commitment)

**Scope:** `links:vote`

RSVP is separate from option voting. Options express *preference* for event details; RSVP expresses *attendance commitment* that drives quorum.

- **Options** = "I prefer these choices" (preference voting)
- **RSVP** = "I'm coming / maybe / not coming" (attendance commitment)

Without RSVP, an agent's votes don't count toward the quorum threshold.

```json
// Request — commit to attending
{
  "action": "rsvp",
  "linkId": "link_001",
  "response": "YES",
  "comment": "Looking forward to it!"
}

// Response — includes updated quorum counts
{
  "ok": true,
  "action": "rsvp",
  "result": {
    "response": { "id": "resp_001", "rsvp": "YES", "comment": "Looking forward to it!" },
    "status": "CONFIRMED",
    "quorum": {
      "yes": 5,
      "maybe": 2,
      "no": 1,
      "total": 10,
      "quorumMin": 4,
      "quorumMet": true
    }
  }
}
```

```json
// Decline attendance
{
  "action": "rsvp",
  "linkId": "link_001",
  "response": "NO",
  "comment": "Can't make it this week"
}
```

SSE event emitted: `rsvp.submitted` with quorum payload.

### 4. Create a POLL Event

**Scope:** `links:create`

```json
// Request
{
  "action": "link.create",
  "destination": "Team Dinner",
  "event_type": "POLL",
  "quorum_min": 4,
  "deadline_at": "2026-03-20T23:59:00Z",
  "enabled_aspects": ["DATE_TIME", "LOCATION", "BUDGET"],
  "participant_variable_aspects": ["DATE_TIME", "LOCATION"],
  "options": [
    {
      "label": "Friday option",
      "aspect_values": [
        { "aspect_type": "DATE_TIME", "data": { "startDate": "2026-03-20", "startTime": "19:00" } },
        { "aspect_type": "LOCATION", "data": { "name": "Restaurant A" } },
        { "aspect_type": "BUDGET", "data": { "min": 200, "max": 400, "currency": "SEK" } }
      ]
    }
  ],
  "invite": ["bob@example.com", "carol@example.com"]
}

// Response
{
  "ok": true,
  "action": "link.create",
  "result": {
    "link_id": "link_002",
    "destination": "Team Dinner",
    "event_type": "POLL",
    "status": "PENDING",
    "share_url": "https://linxtime.com/p/abc123"
  }
}
```

### 5. Create a SIGNUP Event

**Scope:** `links:create`

```json
// Request
{
  "action": "link.create",
  "destination": "React Workshop",
  "event_type": "SIGNUP",
  "capacity": 20,
  "deadline_at": "2026-03-25T23:59:00Z",
  "enabled_aspects": ["DATE_TIME", "SESSION"],
  "options": [
    {
      "label": "Morning Session",
      "aspect_values": [
        { "aspect_type": "SESSION", "data": { "title": "Morning", "capacity": 10, "startTime": "09:00", "endTime": "12:00", "duration": 180 } },
        { "aspect_type": "DATE_TIME", "data": { "startDate": "2026-03-28", "startTime": "09:00", "endTime": "12:00" } }
      ]
    },
    {
      "label": "Afternoon Session",
      "aspect_values": [
        { "aspect_type": "SESSION", "data": { "title": "Afternoon", "capacity": 10, "startTime": "13:00", "endTime": "16:00", "duration": 180 } },
        { "aspect_type": "DATE_TIME", "data": { "startDate": "2026-03-28", "startTime": "13:00", "endTime": "16:00" } }
      ]
    }
  ]
}

// Response
{
  "ok": true,
  "action": "link.create",
  "result": {
    "link_id": "link_003",
    "destination": "React Workshop",
    "event_type": "SIGNUP",
    "status": "ACTIVE",
    "share_url": "https://linxtime.com/s/def456"
  }
}
```

### 6. Register for a Signup Session

**Scope:** `links:vote`

```json
// Request
{
  "action": "signup.register",
  "link_id": "link_003",
  "option_id": "opt_session_morning"
}

// Response
{
  "ok": true,
  "action": "signup.register",
  "result": {
    "registration_id": "reg_001",
    "session_id": "opt_session_morning",
    "status": "CONFIRMED",
    "capacity_remaining": 7
  }
}
```

### 7. Book an Appointment Slot

**Scope:** `links:vote`

```json
// Request
{
  "action": "booking.reserve",
  "link_id": "link_004",
  "option_id": "opt_slot_tuesday_10am"
}

// Response
{
  "ok": true,
  "action": "booking.reserve",
  "result": {
    "booking_id": "bk_001",
    "option_id": "opt_slot_tuesday_10am",
    "status": "CONFIRMED",
    "start_time": "2026-03-25T10:00:00Z",
    "end_time": "2026-03-25T10:30:00Z"
  }
}
```

### 8. Book on Behalf of Someone (Organizer)

**Scope:** `links:manage`

```json
// Request
{
  "action": "booking.reserve_for",
  "link_id": "link_004",
  "option_id": "opt_slot_tuesday_10am",
  "participant_email": "client@example.com",
  "participant_name": "Jane Client"
}

// Response
{
  "ok": true,
  "action": "booking.reserve_for",
  "result": {
    "booking_id": "bk_002",
    "option_id": "opt_slot_tuesday_10am",
    "status": "CONFIRMED",
    "participant_email": "client@example.com",
    "participant_name": "Jane Client"
  }
}
```

### 9. Cancel a Booking

**Scope:** `links:vote`

```json
// Request
{
  "action": "booking.cancel",
  "link_id": "link_004",
  "booking_id": "bk_001"
}

// Response
{
  "ok": true,
  "action": "booking.cancel",
  "result": {
    "booking_id": "bk_001",
    "cancelled": true
  }
}
```

### 10. Cancel a Signup

**Scope:** `links:vote`

```json
// Request
{
  "action": "signup.cancel",
  "link_id": "link_003",
  "option_id": "opt_session_morning"
}

// Response
{
  "ok": true,
  "action": "signup.cancel",
  "result": {
    "session_id": "opt_session_morning",
    "cancelled": true,
    "capacity_remaining": 8
  }
}
```

### 11. Invite Participants

**Scope:** `links:manage`

```json
// Request
{
  "action": "link.invite",
  "link_id": "link_001",
  "participants": ["dave@example.com", "+46701234567"]
}

// Response
{
  "ok": true,
  "action": "link.invite",
  "result": {
    "invited": ["dave@example.com", "+46701234567"],
    "participant_count": 6
  }
}
```

### 12. Post a Comment

**Scope:** `links:vote`

```json
// Request
{
  "action": "comment.add",
  "link_id": "link_001",
  "text": "I can only make it after 18:00 on Friday"
}

// Response
{
  "ok": true,
  "action": "comment.add",
  "result": {
    "comment_id": "cmt_001",
    "text": "I can only make it after 18:00 on Friday",
    "created_at": "2026-03-14T18:30:00Z"
  }
}
```

### 13. Start a Schedule Negotiation

**Scope:** `links:create`

```json
// Request
{
  "action": "negotiate.start",
  "title": "Dinner with friends",
  "participants": ["bob@example.com", "carol@example.com"],
  "time_range": {
    "start": "2026-03-20",
    "end": "2026-03-27"
  },
  "duration_minutes": 120,
  "aspects": ["DATE_TIME", "LOCATION"],
  "deadline": "2026-03-19T18:00:00Z"
}

// Response
{
  "ok": true,
  "action": "negotiate.start",
  "result": {
    "negotiation_id": "neg_001",
    "title": "Dinner with friends",
    "status": "OPEN",
    "participant_count": 3,
    "deadline": "2026-03-19T18:00:00Z"
  }
}
```

### 14. Propose Available Times (Negotiation)

**Scope:** `links:vote`

```json
// Request
{
  "action": "negotiate.propose",
  "negotiation_id": "neg_001",
  "available_times": [
    { "start": "2026-03-21T19:00", "end": "2026-03-21T21:00" },
    { "start": "2026-03-22T15:00", "end": "2026-03-22T17:00" }
  ]
}

// Response
{
  "ok": true,
  "action": "negotiate.propose",
  "result": {
    "negotiation_id": "neg_001",
    "proposals_received": 2,
    "proposals_needed": 3,
    "status": "OPEN"
  }
}
```

### 15. Acknowledge a Notification

**Scope:** `notifications:read`

```json
// Request
{
  "action": "notification.ack",
  "event_id": "evt_001"
}

// Response
{
  "ok": true,
  "action": "notification.ack",
  "result": {
    "acknowledged": true
  }
}
```

### 16. Share a Link

**Scope:** `links:manage`

```json
// Request
{
  "action": "link.share",
  "link_id": "link_003",
  "recipients": ["dave@example.com", "+46701234567"],
  "message": "Check out this workshop, spots are filling up"
}

// Response
{
  "ok": true,
  "action": "link.share",
  "result": {
    "shared_with": ["dave@example.com", "+46701234567"],
    "share_url": "https://linxtime.com/s/def456"
  }
}
```

---

## Event Stream (SSE)

Connect to receive real-time notifications:

```http
GET /v1/agent/stream HTTP/1.1
Host: api.linxtime.com
Authorization: Bearer lt_agent_<token>
Accept: text/event-stream
Last-Event-ID: evt_abc123
```

### Query Parameters

| Parameter | Values | Default | Description |
|---|---|---|---|
| `subscribe` | Comma-separated event categories | all | Server-side filter (e.g. `link_invites,status_changes`) |
| `payload` | `compact`, `full` | `compact` | Compact sends IDs + summary; full sends complete state |

### Event Envelope

```
id: evt_001
event: link.invited
data: {"event_id":"evt_001","type":"link.invited","timestamp":"2026-03-14T18:00:00Z","link":{...},"options":[...],"my_votes":[],"available_actions":["vote","suggest_option","comment"]}
```

### Event Types

| Event Type | Trigger | Notes |
|---|---|---|
| `link.invited` | Agent's user added as participant | Always includes full payload |
| `link.updated` | Link details changed | Link diff + current state |
| `link.status_changed` | Status transition (e.g. PENDING→QUORUM_MET) | Always includes full payload |
| `link.cancelled` | Link cancelled | Summary + reason |
| `option.added` | New option suggested | New option + all options |
| `option.updated` | Option aspect values changed | Updated option |
| `option.removed` | Option deleted | Removed option ID |
| `option.voted` | Someone voted | Option ID + updated counts |
| `option.unvoted` | Vote removed | Option ID + updated counts |
| `option.locked` | Organizer locked winner | Locked option + aspect type |
| `round.opened` | New voting round opened | Round details + options |
| `round.closed` | Voting round closed | Results + winning option |
| `comment.added` | Comment posted | Comment text + author |
| `participant.joined` | Someone joined | Participant info + counts |
| `participant.left` | Someone left | Participant info + counts |
| `signup.registered` | Someone signed up | Session + capacity status |
| `signup.cancelled` | Signup cancelled | Session ID + updated count |
| `signup.capacity_warning` | Session at 80%+ capacity | Session + remaining spots |
| `signup.full` | Session reached capacity | Session ID |
| `rsvp.submitted` | RSVP submitted | Response + updated quorum counts |
| `booking.reserved` | Slot booked | Booking details |
| `booking.cancelled` | Booking cancelled | Booking ID |
| `booking.approval_required` | Booking pending approval | Booking + host info |
| `booking.approved` | Host approved booking | Booking details |
| `booking.rejected` | Host rejected booking | Booking ID + reason |
| `negotiate.started` | Schedule negotiation initiated | Negotiation details + constraints |
| `negotiate.proposal` | Agent proposed times | Aggregated availability |
| `negotiate.resolved` | Consensus reached | Winning time + link created |
| `negotiate.stalled` | No consensus by deadline | Best option + missing agents |

### Keepalive

Server sends `:keepalive\n\n` every 30 seconds. Client should reconnect after 60 seconds of silence.

### Reconnection

On reconnect, include `Last-Event-ID` header. Server replays missed events from a 72-hour buffer.

---

## MCP Tools

When running `linxtime mcp serve`, the following tools are exposed via MCP (stdio transport):

| Tool | Parameters | Maps to |
|---|---|---|
| `linxtime_links_list` | `{ status?: string, limit?: number }` | `GET /v1/agent/links` |
| `linxtime_link_details` | `{ link_id: string }` | `GET /v1/agent/links/:id` |
| `linxtime_options_list` | `{ link_id: string }` | `GET /v1/agent/links/:id/options` |
| `linxtime_rsvp` | `{ link_id: string, response: "YES"\|"MAYBE"\|"NO", comment?: string }` | `rsvp` |
| `linxtime_vote` | `{ link_id: string, option_id: string }` | `option.vote` |
| `linxtime_unvote` | `{ link_id: string, option_id: string }` | `option.unvote` |
| `linxtime_suggest_option` | `{ link_id: string, label: string, aspect_values: AspectValue[] }` | `option.suggest` |
| `linxtime_create_link` | `{ destination: string, event_type: string, ... }` | `link.create` |
| `linxtime_invite` | `{ link_id: string, participants: string[] }` | `link.invite` |
| `linxtime_comment` | `{ link_id: string, text: string }` | `comment.add` |
| `linxtime_inbox` | `{ limit?: number }` | Local daemon inbox query |
| `linxtime_negotiate_start` | `{ title: string, participants: string[], time_range: TimeRange, ... }` | `negotiate.start` |
| `linxtime_negotiate_propose` | `{ negotiation_id: string, available_times: TimeSlot[] }` | `negotiate.propose` |
| `linxtime_signup_register` | `{ link_id: string, option_id: string }` | `signup.register` |
| `linxtime_signup_cancel` | `{ link_id: string, option_id: string }` | `signup.cancel` |
| `linxtime_booking_reserve` | `{ link_id: string, option_id: string }` | `booking.reserve` |
| `linxtime_booking_cancel` | `{ link_id: string, booking_id: string }` | `booking.cancel` |

### MCP Tool Parameter Schemas

**`linxtime_vote`:**
```json
{
  "type": "object",
  "properties": {
    "link_id": { "type": "string", "description": "The link (event) to vote on" },
    "option_id": { "type": "string", "description": "The option to vote for" }
  },
  "required": ["link_id", "option_id"]
}
```

**`linxtime_suggest_option`:**
```json
{
  "type": "object",
  "properties": {
    "link_id": { "type": "string", "description": "The link to suggest an option on" },
    "label": { "type": "string", "description": "Human-readable label for the option" },
    "aspect_values": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "aspect_type": { "type": "string", "enum": ["DATE_TIME", "LOCATION", "BUDGET", "ACTIVITY", "RECURRENCE_PATTERN", "SESSION"] },
          "data": { "type": "object", "description": "Aspect-specific data (see VotableAspect Model section)" }
        },
        "required": ["aspect_type", "data"]
      },
      "description": "Aspect values for the suggested option"
    }
  },
  "required": ["link_id", "label", "aspect_values"]
}
```

**`linxtime_create_link`:**
```json
{
  "type": "object",
  "properties": {
    "destination": { "type": "string", "description": "Event title" },
    "event_type": { "type": "string", "enum": ["POLL", "SIGNUP", "BOOKING"] },
    "quorum_min": { "type": "number", "description": "Minimum participants for quorum (POLL only)" },
    "capacity": { "type": "number", "description": "Total capacity (SIGNUP/BOOKING)" },
    "deadline_at": { "type": "string", "format": "date-time" },
    "enabled_aspects": {
      "type": "array",
      "items": { "type": "string", "enum": ["DATE_TIME", "LOCATION", "BUDGET", "ACTIVITY", "RECURRENCE_PATTERN", "SESSION"] }
    },
    "options": { "type": "array", "description": "Initial options with aspect values" },
    "invite": { "type": "array", "items": { "type": "string" }, "description": "Email/phone to invite" }
  },
  "required": ["destination", "event_type"]
}
```

**`linxtime_negotiate_start`:**
```json
{
  "type": "object",
  "properties": {
    "title": { "type": "string" },
    "participants": { "type": "array", "items": { "type": "string" }, "description": "Email addresses of participants" },
    "time_range": {
      "type": "object",
      "properties": {
        "start": { "type": "string", "format": "date" },
        "end": { "type": "string", "format": "date" }
      },
      "required": ["start", "end"]
    },
    "duration_minutes": { "type": "number" },
    "aspects": { "type": "array", "items": { "type": "string", "enum": ["DATE_TIME", "LOCATION", "BUDGET", "ACTIVITY"] } },
    "deadline": { "type": "string", "format": "date-time" }
  },
  "required": ["title", "participants", "time_range", "duration_minutes"]
}
```

---

## Rate Limits

| Resource | Limit |
|---|---|
| SSE connections per token | 1 (new connection replaces previous) |
| REST write actions | 30/min per token |
| REST read requests | 120/min per token |
| Inbox poll (no daemon) | Min 30s interval enforced |
| SSE keepalive | Server: 30s; Client timeout: 60s |

Rate-limited responses return HTTP 429 with `Retry-After` header.

---

## Links

- **Web App:** https://app.linxtime.com
- **Marketing Site:** https://linxtime.com
- **API Base:** https://api.linxtime.com/v1/agent
- **CLI Package:** `@linxtime/cli` on npm
- **Agent Protocol Spec:** https://linxtime.com/docs/agent-protocol
- **MCP Registry:** Listed as `linxtime` in MCP tool registries
