Developer documentation

We're developers at heart, and as such we wanted to make it as easy as possible to build on top of Owl. Whether you want to create cards programmatically, build a whole new interface for the app, or — hint, hint — use MCP to integrate Owl with your favorite large language model, follow the steps below to get started.

We just open-sourced the Owl MCP server. Integrate Owl with your favorite LLMs via Claude Desktop, Cursor, etc. Download the source code on GitHub.

AnthropicClaudeCursorModel Context Protocol

API reference

Getting started

Getting an API Key

To obtain an API key, log in to your Owl account and navigate to your profile settings. From there, you can create a new API key with a custom name.

Owl API Base URL

All API requests should be made to https://api.owl.cards

Using Your API Key

Include your key in the X-Api-Key header with requests to api.owl.cards:

curl -X GET "https://api.owl.cards/decks" \
  -H "X-Api-Key: your_api_key_here" \
  -H "Content-Type: application/json"
API Operations

Get All Decks

Returns a paginated list of the user's decks.

GET /decks
Query Parameters
  • page - Page number (default: 1)
  • per_page - Items per page (default: 20)
  • sort - Sort field (e.g. "-created_at" for descending)
Response
{
  "items": [
    {
      "id": "string",
      "title": "string",
      "description": "string",
      "created_at": "string",
      "updated_at": "string",
      "archived_at": "string | null",
      "user_id": "string",
      "public": boolean,
      "cards_count": number,
      "cards_in_review_count": number,
      "completion_percentage": number
    }
  ],
  "pagination": {
    "current_page": number,
    "per_page": number,
    "total_pages": number,
    "total_count": number
  }
}

Get a Specific Deck

Returns details for a specific deck.

GET /decks/{deck_id}
Response
{
  "id": "string",
  "title": "string",
  "description": "string",
  "created_at": "string",
  "updated_at": "string",
  "archived_at": "string | null",
  "user_id": "string",
  "public": boolean,
  "cards_count": number,
  "cards_in_review_count": number,
  "completion_percentage": number
}

Get Deck with Cards

Returns a deck with all its cards included.

GET /decks/{deck_id}?include=cards
Response
{
  "id": "string",
  "title": "string",
  "description": "string",
  "created_at": "string",
  "updated_at": "string",
  "archived_at": "string | null",
  "user_id": "string",
  "public": boolean,
  "cards_count": number,
  "cards_in_review_count": number,
  "completion_percentage": number,
  "cards": [
    {
      "id": "string",
      "type": "BasicCard" | "ClozeCard",
      "front": "string", // for BasicCard
      "back": "string",  // for BasicCard
      "text": "string",  // for ClozeCard
      "created_at": "string",
      "updated_at": "string",
      "archived_at": "string | null",
      "stability": number,
      "difficulty": number,
      "reps": number,
      "lapses": number,
      "state": "new" | "learning" | "review" | "relearning",
      "last_reviewed_at": "string | null",
      "next_review_at": "string | null",
      "url": "string",
      "deck_id": "string",
      "deck_title": "string"
    }
  ]
}

Create a Deck

Creates a new deck with the provided details.

POST /decks
Request Body
{
  "title": "string",
  "description": "string",
  "public": boolean, // optional, default: false
  "cards": [ // optional
    {
      "type": "BasicCard",
      "front": "string",
      "back": "string"
    },
    {
      "type": "ClozeCard",
      "text": "string"
    }
  ]
}
Response
{
  "id": "string",
  "title": "string",
  "description": "string",
  "created_at": "string",
  "updated_at": "string",
  "archived_at": "string | null",
  "user_id": "string",
  "public": boolean,
  "cards_count": number,
  "cards_in_review_count": number,
  "completion_percentage": number
}

Update a Deck

Updates an existing deck with the provided changes.

PATCH /decks/{deck_id}
Request Body
{
  "title": "string", // optional
  "description": "string", // optional
  "public": boolean // optional
}
Response
{
  "id": "string",
  "title": "string",
  "description": "string",
  "created_at": "string",
  "updated_at": "string",
  "archived_at": "string | null",
  "user_id": "string",
  "public": boolean,
  "cards_count": number,
  "cards_in_review_count": number,
  "completion_percentage": number
}

Archive a Deck

Archives a deck without deleting it.

POST /decks/{deck_id}/archive

Unarchive a Deck

Restores an archived deck.

POST /decks/{deck_id}/unarchive

Delete a Deck

Permanently deletes a deck and all its cards.

DELETE /decks/{deck_id}

Export a Deck

Exports a deck in a format suitable for importing into other systems.

GET /decks/{deck_id}/export

Code examples

Code Examples
Examples of how to use the Owl API in different programming languages.
// Example: Creating a new deck and adding cards
const API_KEY = 'your_api_key_here';
const BASE_URL = 'https://api.owl.cards';

// Create a new deck
async function createDeck(title, description) {
  const response = await fetch(`${BASE_URL}/decks`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Api-Key': API_KEY
    },
    body: JSON.stringify({
      title,
      description
    })
  });

  return await response.json();
}

// Add a card to a deck
async function addCard(deckId, front, back) {
  const response = await fetch(`${BASE_URL}/decks/${deckId}/cards`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Api-Key': API_KEY
    },
    body: JSON.stringify({
      deck_id: deckId,
      type: 'basic',
      front,
      back
    })
  });

  return await response.json();
}

// Example usage
async function main() {
  try {
    // Create a new deck
    const deck = await createDeck('My Study Deck', 'A deck for studying');
    console.log('Created deck:', deck);

    // Add a card to the deck
    const card = await addCard(deck.id, 'What is the capital of France?', 'Paris');
    console.log('Added card:', card);
  } catch (error) {
    console.error('Error:', error);
  }
}

main();