Scrybert Developer API
Reference for the Scrybert Developer API.
Overview
The Developer API provides programmatic access to a Scrybert account's conversation data and to-do list. For a description of the underlying product behavior, see Features and How It Works. Access to this API is governed by a developer key, distinct from the account's primary credential and limited to the scope described in this document.
Four components are exposed:
- An MCP server, for AI assistants and MCP-compatible clients.
- REST endpoints for pulling conversations by tag.
- A REST endpoint for creating to-do items.
- An outbound webhook system for four account events.
Authentication
All Developer API requests authenticate with a developer key. One key exists per account.
Getting a key
- In the app: Settings > Developer.
- Tap Generate Key.
- The full key is shown once, at generation time. Copy it immediately — it cannot be viewed again.
Generating a new key immediately invalidates the previous one. The server stores only the key's SHA-256 hash and its last four characters, so if the full value is lost, generate a new one rather than trying to recover it.
Key format:
scry_dev_<43 characters, URL-safe base64>
Requests authenticate with a bearer token:
Authorization: Bearer scry_dev_<key>
Key comparison is performed in constant time against the stored hash.
Retention requirement
Key generation and all authenticated requests, except webhook delivery, require the account's Keeper (data retention) setting to be 30 days or longer. At a Keeper setting of 0 days, these requests return 403:
{"detail": "Turn Keeper on for 30 days or longer to use this."}
Webhooks are exempt from this requirement.
MCP server
| Endpoint | https://api.scrybert.com/v1/mcp |
|---|---|
| Method | POST only. GET returns 405. |
| Transport | MCP Streamable HTTP. JSON-RPC 2.0. One response per request; no server-initiated stream. |
| Protocol versions | 2025-06-18, 2025-03-26, 2024-11-05 |
| Authentication | Authorization: Bearer <developer key> |
Seven tools are exposed. Six are read-only; one performs a write.
| Tool | Read-only | Description | Parameters |
|---|---|---|---|
list_conversations | Yes | Returns conversations in reverse chronological order: title, blurb, tags, topics, people, things. | tag (string, exact match, case-insensitive), since, until (date), topic (string, partial match), person, thing (string), limit (integer, max 50) |
get_conversation | Yes | Returns one conversation: summary, action items, list items, and optionally the transcript. | session_id (string, required), include_transcript (boolean) |
search | Yes | Returns conversations matching a query string, with a text snippet per match. | query (string, required, min 2 characters), include_transcripts (boolean), limit (integer, max 50) |
tally | Yes | Returns conversation counts grouped by topic, person, or thing, in descending order. | kind (enum: topics, people, things, required), since, until (date) |
list_tags | Yes | Returns every tag on the account's conversations, standard and custom, with a conversation count per tag, in descending order. | since, until (date) |
list_todos | Yes | Returns to-do items in reverse chronological order. | status (enum: open, done, all), limit (integer, max 200) |
create_todo | No | Creates one to-do item. This is the only tool that modifies account data. | text (string, required, max 500 characters), due (string, YYYY-MM-DD or YYYY-MM-DDTHH:MM) |
A session identifier has the form 20260921-093512, corresponding to the timestamp recording began. Read operations are limited to data retained under the account's Keeper setting. Data outside the scope of this document, including My People, My Things, and Pivotal Moments, is not accessible through this interface.
OAuth authentication, required by claude.ai's and ChatGPT's browser-based connectors, is not implemented. Clients that accept a bearer token directly, including Claude Code, are supported.
Pull conversations by tag
Every conversation carries up to three tags: the standard categories Scrybert assigns (for example Work, Family, Finance) and the account's three custom tags. Where the account holder has changed a conversation's tags in the app, those choices are used. Custom tags are returned under their current names; renaming a custom tag in the app changes it everywhere at once.
List tags
| Endpoint | https://api.scrybert.com/v1/developer/tags |
|---|---|
| Method | GET |
| Authentication | Authorization: Bearer <developer key> |
| Query parameters | since, until (optional, YYYY-MM-DD) |
{
"tags": [ { "tag": string, "conversations": integer }, ... ]
}
Conversations by tag
| Endpoint | https://api.scrybert.com/v1/developer/conversations |
|---|---|
| Method | GET |
| Authentication | Authorization: Bearer <developer key> |
| Query parameters |
tag (optional, exact match, case-insensitive; omit for all conversations),
since, until (optional, YYYY-MM-DD),
limit (optional, default 20, max 100),
full (optional, true adds summary, action items and list items),
transcript (optional, true also adds the transcript)
|
Example:
curl "https://api.scrybert.com/v1/developer/conversations?tag=Work&full=true" \
-H "Authorization: Bearer scry_dev_<key>"
Response (status 200), newest first:
{
"tag": string or null,
"total": integer,
"returned": integer,
"conversations": [
{
"session_id": string,
"when": string,
"title": string,
"blurb": string,
"tags": [string],
"topics": [string],
"people": [string],
"things": [string],
"action_items": integer, or [string] when full=true,
"summary": string (Markdown, full=true),
"list_items": [string] (full=true),
"transcript": string (transcript=true)
}
]
}
Only conversations retained under the account's Keeper setting are returned. An invalid or missing key returns 401; Keeper off returns 403.
To-do endpoint
| Endpoint | https://api.scrybert.com/v1/todos |
|---|---|
| Method | POST |
| Authentication | Authorization: Bearer <developer key> |
Request body:
{
"text": string, required, max 500 characters,
"due": string, optional, "YYYY-MM-DD" or "YYYY-MM-DDTHH:MM",
"source": string, optional, max 40 characters, default "API",
"id": string, optional, max 128 characters
}
Response, new item (status 201):
{
"uid": string,
"text": string,
"due": string or null,
"source": string,
"created_at": integer (unix ms),
"duplicate": false
}
If id is supplied and matches a previous request, the endpoint returns status 200 with "duplicate": true and the original item, rather than creating a second item.
An invalid due value returns 422. A missing or empty text value returns 422.
Rate limit: 120 creations per account per hour, tracked in memory. Requests exceeding this limit return 429.
Items created through this endpoint appear on the account's device on the next synchronization cycle, which occurs on application open or at intervals not exceeding 15 minutes.
Webhooks
Four account events are available for subscription. Each event has an independent enabled state and target URL, configured in Settings > Developer.
conversation.summarized |
transcript.received |
recap.created |
todo.created |
Payload envelope
{
"id": string (uuid, unique per delivery attempt group),
"event": string,
"test": boolean,
"created_at": string (ISO 8601, UTC),
"data": object
}
Request headers
Content-Type: application/json
User-Agent: Scrybert-Webhooks/1
Scrybert-Event: <event name>
Scrybert-Delivery: <delivery id>
Scrybert-Signature: t=<unix timestamp>,v1=<hex-encoded HMAC-SHA256>
Signature verification
Each account has one webhook signing secret, prefixed whsec_, available in Settings > Developer. The v1 value is computed as:
HMAC-SHA256(key = secret, message = "{t}." + raw_request_body)
Reference implementation:
import hmac, hashlib
def verify(secret: str, header: str, body: bytes) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
t, v1 = parts["t"], parts["v1"]
expected = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, v1)
The signature must be computed over the raw request body as received. Re-serializing the payload before verification can alter key order or whitespace and will produce a mismatch.
Delivery behavior
| Retry schedule | Immediate, then +5 seconds, then +30 seconds (maximum 3 attempts) |
|---|---|
| Retry conditions | Network error or response status 5xx |
| Terminal conditions | Response status 4xx is not retried |
| Timeout | 5 seconds per attempt |
| Redirects | Not followed |
| Target address requirements | https only; must resolve to a public IP address at both save time and delivery time |
Pivotal Moments data is not included in any webhook payload under any event.
Rate limits and requirements
| Keeper requirement, MCP and to-do endpoint | 30 days or longer |
|---|---|
| Keeper requirement, webhooks | None |
| To-do creation rate limit | 120 per account per hour, shared across MCP and the to-do endpoint |
list_conversations / search result limit | 50 maximum |
list_todos result limit | 200 maximum |
| Webhook delivery timeout | 5 seconds per attempt, 3 attempts maximum |
Status codes
200 | Request succeeded. For the to-do endpoint with a repeated id, indicates the original item was returned. |
|---|---|
201 | A new item was created. |
401 | Missing or invalid developer key. |
403 | Keeper setting below the required threshold. |
405 | Method not allowed (for example, GET on the MCP endpoint). |
409 | Required configuration missing (for example, testing a webhook with no saved address). |
422 | Request body failed validation. |
429 | Rate limit exceeded. |
Support
For issues not covered by this document, see Contact.