Skip to content

API

Teak provides a public API at https://api.teakvault.com/v1.

  • Production: https://api.teakvault.com/v1
  • Local development: http://api.teak.localhost:1355/v1
  • OpenAPI: https://api.teakvault.com/openapi.json

Teak also exposes a remote MCP server:

  • Production: https://api.teakvault.com/mcp
  • Local development: http://api.teak.localhost:1355/mcp

See the dedicated MCP docs for tool contracts and JSON-RPC examples.

All API requests require your Teak API key in the Authorization header:

Terminal window
Authorization: Bearer teakapi_secret_live_a1b2c3d4_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Generate and manage API keys in Teak Settings: https://app.teakvault.com/settings.

  • X-Request-Id is returned on every gateway response.
  • Idempotency-Key is supported on POST /v1/cards and POST /v1/cards/bulk.
  • Rate-limited responses may include RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, and Retry-After.
GET /healthz
{
"status": "ok",
"service": "teak-api",
"version": "v1"
}
GET /v1

Response includes the current v1 route list plus MCP connection details.

GET /v1/cards?q=design&limit=25&cursor=opaque&include=content,metadata

Supported query parameters:

  • q
  • limit
  • cursor
  • sort=newest|oldest
  • type
  • tag
  • favorited=true|false
  • createdAfter
  • createdBefore
  • include=content,metadata,processing

The include parameter is a comma-separated list of field groups to add to each item. Omitting it returns the base fields only.

include valueExtra fields added
contentcontent, notes, aiSummary
metadatafileUrl, linkPreviewImageUrl, screenshotUrl, thumbnailUrl
processingprocessingStatus, metadataStatus, aiTranscript

Response:

{
"items": [
{
"id": "card_1",
"appUrl": "https://app.teakvault.com/?card=card_1",
"createdAt": 1,
"isFavorited": true,
"metadataTitle": "Example",
"tags": ["design"],
"type": "link",
"updatedAt": 2,
"url": "https://example.com"
}
],
"pageInfo": {
"hasMore": false,
"nextCursor": null
}
}
POST /v1/cards
Content-Type: application/json
Idempotency-Key: save-link-123

Supported request body fields:

  • content — text or URL to save (required if url is not set)
  • url — URL to save (required if content is not set). Must be an http or https URL.
  • notes — optional freeform notes attached to the card (string or null)
  • tags — optional array of tag strings
  • source — optional string identifying the client or integration (e.g. "raycast_quick_save")
{
"content": "https://teakvault.com",
"tags": ["design"]
}
{
"status": "created",
"cardId": "card_1",
"appUrl": "https://app.teakvault.com/?card=card_1"
}
POST /v1/cards/bulk
Content-Type: application/json
Idempotency-Key: bulk-ops-001
{
"operation": "favorite",
"items": [{ "cardId": "card_1", "isFavorited": true }]
}

Supported operations:

  • create
  • update
  • favorite
  • delete

A single request accepts at most 100 items. Requests with an empty items array, or more than 100 items, are rejected with 400 INVALID_INPUT. Split larger batches across multiple requests.

Response:

{
"operation": "favorite",
"results": [{ "index": 0, "status": "success", "cardId": "card_1" }],
"summary": { "total": 1, "succeeded": 1, "failed": 0 }
}
GET /v1/cards/changes?since=1710000000000&limit=50
{
"items": [],
"deletedIds": ["card_2"],
"pageInfo": {
"hasMore": false,
"nextCursor": null
}
}

These remain stable for existing consumers:

GET /v1/cards/search?q=design&limit=50
GET /v1/cards/favorites?q=design&limit=50

Supported filters on both legacy endpoints:

  • q
  • limit
  • type
  • tag
  • sort
  • favorited
  • createdAfter
  • createdBefore

Legacy response shape remains:

{
"items": [],
"total": 0
}
GET /v1/cards/:cardId

Returns the full card object.

PATCH /v1/cards/:cardId
Content-Type: application/json

Allowed fields:

  • content
  • url — must be an http or https URL
  • notes
  • tags

Example:

{
"notes": "Updated note",
"tags": ["reference"]
}
DELETE /v1/cards/:cardId

Response: 204 No Content

PATCH /v1/cards/:cardId/favorite
Content-Type: application/json
{
"isFavorited": true
}
GET /v1/tags
{
"items": [{ "name": "design", "count": 3 }]
}

Errors preserve the stable { code, error } shape. Some responses may also include requestId, details, or retryAt.

{
"code": "INVALID_INPUT",
"error": "Body must include `content` or `url`"
}

When using Idempotency-Key on POST /v1/cards or POST /v1/cards/bulk, two 409 Conflict scenarios may occur:

Situationcodeerror
A request with the same key is still in flightCONFLICTIdempotency-Key is already being processed
The key was already used with a different request bodyCONFLICTIdempotency-Key was already used with a different request

If the key was previously used with an identical request body, the original response is replayed without re-processing the operation.