API

Public Teak API for creating, querying, and syncing cards

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

Base URLs

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

MCP Endpoint

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.

Authentication

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

Authorization: Bearer teakapi_xxx_xxx

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

Headers

  • 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.

Endpoints

Service Health

GET /healthz
{
  "status": "ok",
  "service": "teak-api",
  "version": "v1"
}

API Info

GET /v1

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

Canonical Card Listing

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
  }
}

Create Card

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)
  • 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"
}

Bulk Card Operations

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

Response:

{
  "operation": "favorite",
  "results": [{ "index": 0, "status": "success", "cardId": "card_1" }],
  "summary": { "total": 1, "succeeded": 1, "failed": 0 }
}

Incremental Sync

GET /v1/cards/changes?since=1710000000000&limit=50
{
  "items": [],
  "deletedIds": ["card_2"],
  "pageInfo": {
    "hasMore": false,
    "nextCursor": null
  }
}

Legacy Search Endpoints

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 Card

GET /v1/cards/:cardId

Returns the full card object.

Update Card

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

Allowed fields:

  • content
  • url
  • notes
  • tags

Example:

{
  "notes": "Updated note",
  "tags": ["reference"]
}

Delete Card

DELETE /v1/cards/:cardId

Response: 204 No Content

Favorite State

PATCH /v1/cards/:cardId/favorite
Content-Type: application/json
{
  "isFavorited": true
}

Tags

GET /v1/tags
{
  "items": [{ "name": "design", "count": 3 }]
}

Errors

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`"
}

Idempotency Key Errors

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.