EventStrand API
EventStrand exposes a REST API for reading and writing strand and event data. Use it to build agents, integrations, and tooling on top of the open .rcal format.
All endpoints return JSON. Authentication uses API keys issued from your account settings. The API is the same interface the EventStrand web app uses — there are no private endpoints.
Base URL
All API requests are made to:
Endpoints are prefixed with /api/. For example, the upcoming events endpoint is at https://api.eventstrand.com/api/dashboard/upcoming.
Rate Limits
Auth endpoints are limited to 30 requests per 15-minute window. All other /api/ endpoints are limited to 200 requests per minute per IP. Limits apply across API key and session token usage equally.
Responses include standard rate limit headers. When exceeded, the API returns 429 Too Many Requests.
Authentication — API Keys
API keys allow agents and external tools to authenticate without a user session. Generate keys from your EventStrand account under Account → API Keys.
All keys are prefixed with esk_ and stored as SHA-256 hashes server-side. Revoking a key is immediate — any tool using it loses access instantly.
Scopes
Each key carries one or more scopes that determine what it can access. Request only the scopes your use case requires.
Using a Key
Pass the key as a Bearer token in the Authorization header on every request:
Authorization: Bearer esk_your_key_here
// Example: fetch upcoming events with an API key
const res = await fetch('https://api.eventstrand.com/api/dashboard/upcoming?days=7', {
headers: {
'Authorization': `Bearer ${process.env.EVENTSTRAND_API_KEY}`
}
});
const data = await res.json();
// data.events — array of upcoming event objects
REST API
Upcoming Events
Required scope: portal:read
| Parameter | Type | Required | Description |
|---|---|---|---|
| days | integer | optional | Days ahead to include. Min 1, max 365. Default 60. |
| workspaceId | string | optional | Filter to a specific workspace. Omit for all workspaces. |
{
"events": [
{
"title": "Friday Jazz Night",
"date": "2026-05-02",
"time": "21:00",
"venue": "The Rusty Nail",
"address": "123 W Division St, Chicago",
"strandId": "664f...",
"strandTitle": "The Rusty Nail",
"publisher": "rusty-nail",
"color": "#6C8FFF",
"vibes": ["mellow", "social"]
}
]
}
Subscriptions
Required scope: subscriptions:read
| Parameter | Type | Required | Description |
|---|---|---|---|
| workspaceId | string | optional | Filter results to a specific workspace. |
{
"strands": [
{
"_id": "664f...",
"title": "The Rusty Nail",
"venue": "The Rusty Nail",
"publisherHandle": "rusty-nail",
"color": "#6C8FFF"
}
],
"braids": []
}
My Strands
Required scope: strand:read
Public Strand
passcode query param if you have one. If :handle is an old handle the publisher has since changed, this returns {"redirect": "newhandle"} instead of strand data — re-request with the returned handle.| Parameter | Type | Required | Description |
|---|---|---|---|
| handle | string | required | Publisher handle (e.g. rusty-nail). |
| strandId | string | required | The strand's ID. |
| passcode | string | optional | Required for protected strands. |
{
"strand": { "..." },
"publisherHandle": "rusty-nail",
"subscribed": false
}
{
"redirect": "new-handle"
}
redirect response above instead of a 404 — your client needs to follow it and retry with the new handle. Beyond those 10, an old handle 404s as Publisher not found. Prefer looking up and storing the strand ID for anything you keep long-term; handles still need to be current to resolve it.
Generate API Key
{
"label": "My Agent",
"scopes": ["portal:read", "strand:read"]
}
{
"id": "664f...",
"label": "My Agent",
"prefix": "esk_a1b2c3d4",
"scopes": ["portal:read", "strand:read"],
"createdAt": "2026-04-26T12:00:00.000Z",
"key": "esk_a1b2c3d4..." // shown once — store immediately
}
List API Keys
Revoke API Key
MCP Server Coming Soon
EventStrand will expose a Model Context Protocol server, allowing any MCP-compatible agent or AI tool to connect to your EventStrand data natively — without custom integration code.
Once available, you'll be able to add EventStrand as a connector in Claude, Claude Code, Cursor, or any MCP-compatible client by pointing it at the server URL.
Planned Tools
The MCP server will expose the following tools to agents:
Blue = attendee read · Purple = venue write
OAuth 2.0
EventStrand implements OAuth 2.0 with the Authorization Code flow. This is the authentication mechanism used when connecting EventStrand to Claude.ai or any MCP-compatible client that needs to act on behalf of a user.
If you are building an agent integration using API keys directly, you do not need OAuth — see API Keys above. OAuth is required for the Claude.ai connector and any third-party app that needs delegated user authorization.
https://claude.ai/api/mcp/auth_callback and https://claude.com/api/mcp/auth_callback. Both must be registered when setting up a client.
Authorization Endpoint
redirect_uri with a short-lived authorization code.| Parameter | Type | Required | Description |
|---|---|---|---|
| client_id | string | required | Your OAuth application client ID. |
| redirect_uri | string | required | Must exactly match a registered callback URL for your client. |
| response_type | string | required | Must be code. |
| scope | string | required | Space-separated list of scopes. See Scopes below. |
| state | string | recommended | Random value to protect against CSRF. Returned unchanged in the callback. |
GET https://api.eventstrand.com/api/oauth/authorize
?client_id=your_client_id
&redirect_uri=https://claude.ai/api/mcp/auth_callback
&response_type=code
&scope=portal:read+strand:read
&state=random_csrf_token
Token Endpoint
Exchange authorization code:
{
"grant_type": "authorization_code",
"code": "the_auth_code",
"redirect_uri": "https://claude.ai/api/mcp/auth_callback",
"client_id": "your_client_id",
"client_secret": "your_client_secret"
}
{
"access_token": "esk_...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "ref_...",
"scope": "portal:read strand:read"
}
Scopes
Request only the scopes your application requires. The user sees each scope on the consent screen before approving.
Refreshing Tokens
Access tokens expire after 1 hour. Use the refresh token to obtain a new one without requiring the user to re-authorize.
{
"grant_type": "refresh_token",
"refresh_token": "ref_...",
"client_id": "your_client_id",
"client_secret": "your_client_secret"
}