A Custom OpenClaw Skill for TodayIngoLearned

In 2020, I developed TodayIngoLearned (blog post), a personal knowledge and learning management system based on the idea of storing the things I learn during the day.

Since then, the project (see GitHub) has slowly evolved – but strictly as a private project which only ever gets updated when I need or want a new feature.

As one does, I have been playing around with OpenClaw a lot lately, and as part of that, integrating TodayIngoLearned into my OpenClaw agent was quite obvious. Hence, I want to take this opportunity to share how to build a simple custom Skill for OpenClaw, focused on an equally simple REST API.

The Prerequisite: The REST API

Of course, a REST API is not necessarily a prerequisite for an agent skill, but it makes life a lot simpler. In the case of TodayIngoLearned, a very simple API already exists.

The three relevant features are searching, retrieving, and creating so-called “TILs” – entries in the knowledge database.

Using curl this looks something like this:

Searching

curl -H "X-API-Key: til_XXX" "https://todayingolearned.til/api/v1/til/search?type=title&q=openclaw"

Retrieving

curl -H "X-API-Key: til_XXX" https://todayingolearned.til/api/v1/til/1

Creating

curl -X POST https://todayingolearned.til/api/v1/til ^
  -H "X-API-Key: til_XXX" ^
  -H "Content-Type: application/json" ^
  -d "{\"title\": \"OpenClaw\", \"description\": \"It's an agent!\", \"date\": \"2026-03-22\"}"

As you might have realized, this is not a full CRUD API. This is on purpose as I don’t (yet) want my agent(s) to be able to make changes or delete content.

Developing Custom OpenClaw Skill

An OpenClaw Skill, at the end of the day, is a directory with a SKILL.md markdown file and potentially some additional tools and helpers. This makes sense as OpenClaw follows the Agent Skills standard.

Hence, on a file level, the TodayIngoLearned Skill looks like this:

* todayingolearned
    * SKILL.md
    * scripts
        * til_create.sh
        * til_get.sh
        * til_search.sh

After some testing, I decided to create scripts for the key actions instead of just relying on providing curl-examples in the SKILL.md.

SKILL.md

---
name: todayingolearned
description: Read, create, and search learnings in the TodayIngoLearned personal knowledge platform via its HTTP API.
metadata:
  openclaw:
    primaryEnv: TIL_API_KEY
---

# TodayIngoLearned

Use this skill to read, create, and search "learnings" in the TodayIngoLearned API.

## When to use

- The user wants to save a new learning / TIL
- The user says they learned something and wants it stored
- The user wants to fetch a learning by ID
- The user wants to search existing learnings
- The user mentions TodayIngoLearned or `todayingolearned.til`

## Configuration

This skill expects:

- `TIL_API_KEY`
- optional `TIL_BASE_URL` which defaults to `https://todayingolearned.til`

## API

Create learning:

- `POST /api/v1/til`
- JSON body:
  - `title`
  - `description`
  - `date` in `YYYY-MM-DD`

Read learning by ID:

- `GET /api/v1/til/{id}`

Search learnings:

- `GET /api/v1/til/search?type=TYPE&q=QUERY`
- supported `type` values:
  - `title`
  - `tag`

## Workflows

### Create a learning

When the user wants to store a learning:

1. Infer or ask for:
   - title
   - description
   - date
2. Default `date` to today if the user does not specify one.
3. Call:

    bash scripts/til_create.sh "TITLE" "DESCRIPTION" "YYYY-MM-DD"

4. Summarize the result briefly.

### Read a learning

When the user wants to retrieve a learning by ID:

1. Extract the numeric ID.
2. Call:

    bash scripts/til_get.sh "ID"

3. Return the learning in readable form.

### Search learnings

When the user wants to search existing learnings:

1. Determine the search type:
   - use `title` for normal keyword search
   - use `tag` for hashtag search such as `#misc`
2. Call:

    bash scripts/til_search.sh "TYPE" "QUERY"

3. Summarize the results clearly.
4. Preserve hashtags exactly when doing tag searches.

## Examples

### Create

    bash scripts/til_create.sh "My TIL" "Learned something about curl #misc" "2026-03-21"

### Read

    bash scripts/til_get.sh "1"

### Search by title

    bash scripts/til_search.sh "title" "javascript"

### Search by tag

    bash scripts/til_search.sh "tag" "#misc"

## Notes

- Prefer concise titles when creating entries.
- Keep hashtags like `#misc` in the description if the user includes them.
- For tag searches, include the leading `#`.
- If the API returns an error, explain it clearly.
- If the user does not specify a date when creating a learning, use today's date.

As you can see, a “skill” is essentially a narrative description, including examples, on how to do things. Having the scripts available makes this a little bit easier, as there is no confusion about the correct curl calls.

A key point here is the primaryEnv: TIL_API_KEY in the header. This allows us to set the API key within OpenClaw so that we don’t have to worry about either hardcoding it or managing .env files ourselves.

Scripts

Below are the three helper scripts. While not strictly necessary and quite boring, they reduce the surface area for potential mistakes.

til_create.sh

#!/usr/bin/env bash
set -euo pipefail

TITLE="${1:-}"
DESCRIPTION="${2:-}"
DATE_VALUE="${3:-}"

if [[ -z "$TITLE" || -z "$DESCRIPTION" || -z "$DATE_VALUE" ]]; then
  echo "Usage: $0 <title> <description> <date>" >&2
  exit 1
fi

API_BASE="${TIL_BASE_URL:-https://todayingolearned.til}"
API_KEY="${TIL_API_KEY:?TIL_API_KEY is required}"

curl --silent --show-error --fail \
  -X POST "${API_BASE%/}/api/v1/til" \
  -H "X-API-Key: ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d "$(jq -n \
    --arg title "$TITLE" \
    --arg description "$DESCRIPTION" \
    --arg date "$DATE_VALUE" \
    '{title: $title, description: $description, date: $date}')"

til_get.sh

#!/usr/bin/env bash
set -euo pipefail

ID="${1:-}"

if [[ -z "$ID" ]]; then
  echo "Usage: $0 <id>" >&2
  exit 1
fi

API_BASE="${TIL_BASE_URL:-https://todayingolearned.til}"
API_KEY="${TIL_API_KEY:?TIL_API_KEY is required}"

curl --silent --show-error --fail \
  -H "X-API-Key: ${API_KEY}" \
  "${API_BASE%/}/api/v1/til/${ID}"

til_search.sh

#!/usr/bin/env bash
set -euo pipefail

TYPE="${1:-}"
QUERY="${2:-}"

if [[ -z "$TYPE" || -z "$QUERY" ]]; then
  echo "Usage: $0 <type> <query>" >&2
  exit 1
fi

case "$TYPE" in
  title|tag)
    ;;
  *)
    echo "Error: type must be 'title' or 'tag'" >&2
    exit 1
    ;;
esac

API_BASE="${TIL_BASE_URL:-https://todayingolearned.til}"
API_KEY="${TIL_API_KEY:?TIL_API_KEY is required}"

curl --silent --show-error --fail \
  -G "${API_BASE%/}/api/v1/til/search" \
  -H "X-API-Key: ${API_KEY}" \
  --data-urlencode "type=${TYPE}" \
  --data-urlencode "q=${QUERY}"

A Note on Model Quality

I am currently also testing various (open) models with OpenClaw. When using the otherwise really great Mistral Small 3.2 24B Instruct, I was unable to get the skill to work consistently. Switching to the more powerful OpenAI GPT OSS 120B – simply because I had it available on my inference service – solved this issue immediately.

While this is absolutely to be expected, it highlights that we have entered a stage of AI/LLM/agent development in which the question of how much “intelligence” is needed, given cost, environmental impact, etc., for a given task is becoming increasingly important and practical.

Conclusion

While it sounds highly technical, agent skills are “just” bundled prompts and tools – and that is a good thing. The technical barrier to building custom skills is relatively low, and creating custom skills, in many cases, is primarily about clearly defining problems, use cases, and examples rather than software development.

The cool thing: Even though it is relatively easy to create custom skills, this approach, given powerful enough models, actually works and makes agents like OpenClaw useful. With only a few lines of text and code that can be easily generated, I now have a convenient conversational interface for my knowledge/learning management system, built on pre-existing APIs.