xmark API
chat with your x bookmarks
base URL
https://www.xmark.dev/api/v1the xmark API uses REST conventions with JSON request and response bodies. all endpoints require authentication via API key.
responses follow a consistent shape: { data: ... } for success and { error: { code, message, requestId } } for errors.
authentication
all API requests must include your API key in the Authorization header. keys are prefixed with nt_live_ and shown exactly once at creation: store them in a secret manager, never commit them.
header format
Authorization: Bearer nt_live_YOUR_API_KEYverify your key
any valid key can call GET /keys/me to introspect itself, useful for confirming a key works and seeing its scopes.
curl "https://www.xmark.dev/api/v1/keys/me" \
-H "Authorization: Bearer nt_live_YOUR_API_KEY"scopes
each API key carries a set of scopes. grant the minimum needed. an admin key can create other keys and manage webhooks, so treat it as a root credential.
| scope | description |
|---|---|
| read | read-only access to owned resources |
| write | create and update owned resources |
| delete | delete owned resources |
| admin | manage API keys and webhook endpoints |
default scopes (if none specified): read, write.
API reference
bookmarks
read, search, and sync the authenticated user's X bookmarks.
paginated list of the caller's bookmarks, newest first
fetch a single bookmark
remove a bookmark. local-only by default (the next sync re-creates anything still bookmarked on X); pass unbookmark=true to also remove it on X
semantic search over the caller's bookmark library (pgvector + OpenAI embeddings)
pull the latest bookmarks from the connected X account, embed new ones, and store them. large libraries sync in chunks: call again while done is false. when X rate-limits the crawl (10 req / 15 min) the response carries retryAfterSeconds: wait that long before the next call, and the sync resumes where it stopped
chat
ask natural-language questions about bookmarks; Claude answers with citations grounded in them.
submit a question. xmark retrieves the most relevant bookmarks via semantic search and has Claude generate an answer with inline citations. returns a single JSON response
conversations
list, read, and delete past chat conversations.
paginated list of the caller's conversations, most recently active first
the conversation's message history, oldest first. assistant messages carry the IDs of the bookmarks retrieved for that answer
permanently delete a conversation and its messages
usage
plan and quota usage for the authenticated user.
the caller's plan, chat-message usage this month, syncs today, bookmark count, and whether an X account is connected. limit and remaining are null on unlimited plans
insights
bookmark analytics: velocity, top authors/topics, early calls, and follow recommendations.
headline stats, weekly bookmark velocity, top authors and topics, 'early calls' (bookmarks saved early that gained traction), and follow recommendations. early calls + recommendations are tier-limited (free: 3, paid: 10)
notifications
the caller's notification inbox.
paginated list of the caller's notifications, newest first
mark a notification read
delete a notification
mark all of the caller's unread notifications read
connections
connect and disconnect the caller's X account (native OAuth).
start native X OAuth: returns an authorization URL to open in an ASWebAuthenticationSession. the backend callback exchanges the code server-side and redirects to the app scheme
disconnect the X account (revoke + clear stored OAuth tokens). reconnect via /connections/x/connect
account
profile, onboarding, settings, and notification preferences.
the authenticated user's profile + onboarding state (used for post-login routing)
mark onboarding complete
delete the account (soft-delete data, cancel any subscription)
the caller's email-notification preference
update the email-notification preference
the caller's profile settings (auto-cleanup window, public-profile toggle, username)
update profile settings
billing
subscription management and Apple receipt verification.
a billing-management URL: a pre-authenticated Stripe customer portal for subscribers, or the web billing page
verify a StoreKit 2 signed transaction server-side and grant the plan
referrals
the 'share your wrapped on X for 2 months free' referral.
the caller's referral / wrapped-share bonus status
post the wrapped summary to the connected X account and grant the one-time bonus
wrapped
the caller's 'xmark wrapped' aggregate stats.
wrapped stats: totals, top authors, top topics, languages
API keys
manage API keys programmatically. key-management endpoints require the admin scope.
list the caller's API keys, newest first
create a new API key: the raw key is returned once
introspect the calling API key (no scope required; JWT principals get 404)
fetch a specific key's metadata
rename a key
revoke a key (204 No Content)
rotate a key: revoke it and mint a replacement with the same name and scopes
webhooks
developer webhook endpoints with signed deliveries. all endpoints require the admin scope.
list the caller's webhook endpoints, newest first
create a webhook endpoint: the signing secret is returned once
fetch a webhook endpoint
update url, events, or active
delete a webhook endpoint (204 No Content)
paginated delivery log, newest first
rotate the signing secret: the new secret is returned once
fire a signed test event and report the delivery outcome
changelog
machine-readable release history - public, no API key required.
release history newest-first: version, date, type (feature/fix/breaking/docs), summary, and migration notes on breaking entries. public - the one-line X-Notice header on every response is the latest entry's summary; this is the structured history behind it
error handling
errors are returned as JSON with code, message, optional field, and requestId. the X-Request-ID response header carries a per-request trace id: include it when contacting support.
{
"error": {
"code": "SERVER_005",
"message": "name must be under 100 characters",
"field": "name",
"requestId": "req_abc123"
}
}| code | HTTP | description |
|---|---|---|
| AUTH_001 | 401 | missing or invalid authorization header |
| AUTH_002 | 403 | API key missing required scope |
| AUTH_004 | 401 | invalid or revoked API key |
| code | HTTP | description |
|---|---|---|
| BILLING_001 | 402 | no active subscription |
| BILLING_006 | 402 | plan usage limit reached (402 on chat quota, 429 on sync rate) |
| BILLING_007 | 402 | subscription required for this operation |
| BILLING_008 | 403 | current plan does not include this feature |
| code | HTTP | description |
|---|---|---|
| RATE_001 | 429 | too many requests - back off and retry |
| code | HTTP | description |
|---|---|---|
| SERVER_001 | 500 | internal server error |
| SERVER_002 | 400 | bad request (malformed body or missing field) |
| SERVER_003 | 404 | resource not found |
| SERVER_004 | 405 | method not allowed |
| SERVER_005 | 400 | validation failed |
| SERVER_008 | 500 | database error |
| SERVER_012 | 409 | conflict (e.g. concurrent modification) |
| code | HTTP | description |
|---|---|---|
| HOOK_001 | 401 | invalid webhook signature |
| HOOK_002 | 500 | webhook processing failed |
| HOOK_004 | 408 | webhook delivery timed out |