Patterns & Recipes

Build a Task Bot with the REST API

Step-by-step walkthrough: create an API key, make your first request, and build a Python script that posts overdue task reminders

6 min read · Intermediate

This tutorial walks you through building a simple task bot that checks for overdue tasks and posts reminders to a channel. You'll start with raw curl commands to understand the API, then wrap everything into a Python script you can run on a cron.

By the end you'll have a working integration that talks to the REST API documented at /docs/api.

Prerequisites

  • A Saltare workspace (any plan)
  • A channel to post reminders in (e.g. #general)
  • Python 3.8+ with requests installed, or just curl to follow along manually

Step 1: Create an API key

  1. Open your workspace and go to Settings → API Keys
  2. Click Create API Key
  3. Name it something descriptive: task-reminder-bot
  4. Select these scopes:
    • tasks:read — to list overdue tasks
    • messages:write — to post reminders
    • channels:read — to look up the target channel
  5. Leave Agent binding empty (this bot posts as your user, not as an agent)
  6. Click Create and copy the sk_sal_… token immediately — you won't see it again

Export it in your terminal for the rest of this tutorial:

export SALTARE_API_KEY="sk_sal_your_token_here"
export SALTARE_URL="https://app.saltare.com"

Step 2: Verify the key works

The /api/v1/me endpoint confirms your identity and workspace:

curl -s "$SALTARE_URL/api/v1/me" \
  -H "Authorization: Bearer $SALTARE_API_KEY" | python3 -m json.tool

You should see something like:

{
  "workspace": {
    "name": "Acme Corp",
    "slug": "acme-corp"
  },
  "user": {
    "name": "Jane Smith",
    "email": "[email protected]"
  },
  "scopes": ["tasks:read", "messages:write", "channels:read"]
}

If you get a 401, double-check the token. If you get a 403, make sure the key hasn't expired or been revoked.

Step 3: List tasks

Fetch all open tasks:

curl -s "$SALTARE_URL/api/v1/tasks?state=open" \
  -H "Authorization: Bearer $SALTARE_API_KEY" | python3 -m json.tool

The response includes pagination headers. For a small workspace you'll get everything in one page. For larger ones, check X-Total-Pages and pass ?page=2 to paginate.

Filter to overdue tasks by combining state with due_before:

curl -s "$SALTARE_URL/api/v1/tasks?state=open&due_before=$(date -u +%Y-%m-%d)" \
  -H "Authorization: Bearer $SALTARE_API_KEY" | python3 -m json.tool

Each task in the response looks like:

{
  "slug": "fix-login-bug",
  "title": "Fix login redirect loop",
  "state": "open",
  "due_date": "2026-04-14",
  "assignee": {
    "type": "User",
    "name": "Jane Smith"
  }
}

Step 4: Find your target channel

Look up the channel slug you want to post reminders to:

curl -s "$SALTARE_URL/api/v1/channels" \
  -H "Authorization: Bearer $SALTARE_API_KEY" | python3 -m json.tool

Find the channel you want (e.g. general) and note the slug value.

Step 5: Post a message

Try a manual message first:

curl -s -X POST "$SALTARE_URL/api/v1/messages" \
  -H "Authorization: Bearer $SALTARE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_slug": "general",
    "body": "Hello from the API!"
  }' | python3 -m json.tool

You should see the message appear in the channel. If you get a 422, check that the channel slug is correct and your key has the messages:write scope.

Step 6: Put it together in Python

Here's a complete script that checks for overdue tasks and posts a summary:

#!/usr/bin/env python3
"""Overdue task reminder bot for Saltare."""

import os
from datetime import date

import requests

API_KEY = os.environ["SALTARE_API_KEY"]
BASE_URL = os.environ.get("SALTARE_URL", "https://app.saltare.com")
CHANNEL = os.environ.get("SALTARE_CHANNEL", "general")

HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}


def get_overdue_tasks():
    """Fetch all open tasks past their due date."""
    today = date.today().isoformat()
    tasks = []
    page = 1

    while True:
        resp = requests.get(
            f"{BASE_URL}/api/v1/tasks",
            headers=HEADERS,
            params={
                "state": "open",
                "due_before": today,
                "page": page,
                "per_page": 50,
            },
        )
        resp.raise_for_status()
        batch = resp.json()
        if not batch:
            break
        tasks.extend(batch)
        total_pages = int(resp.headers.get("X-Total-Pages", 1))
        if page >= total_pages:
            break
        page += 1

    return tasks


def post_reminder(tasks):
    """Post an overdue task summary to the configured channel."""
    if not tasks:
        return

    lines = [f"**{len(tasks)} overdue task(s)** as of {date.today().isoformat()}:\n"]

    for task in tasks:
        assignee = task.get("assignee", {}).get("name", "Unassigned")
        due = task.get("due_date", "no date")
        lines.append(f"- **{task['title']}** (due {due}, assigned to {assignee})")

    body = "\n".join(lines)

    resp = requests.post(
        f"{BASE_URL}/api/v1/messages",
        headers=HEADERS,
        json={"channel_slug": CHANNEL, "body": body},
    )
    resp.raise_for_status()
    print(f"Posted reminder with {len(tasks)} task(s)")


if __name__ == "__main__":
    overdue = get_overdue_tasks()
    if overdue:
        post_reminder(overdue)
    else:
        print("No overdue tasks — nothing to post")

Run it:

pip install requests
python task_reminder.py

Step 7: Schedule it

Run the script daily with cron:

# Edit your crontab
crontab -e

# Add this line (runs weekdays at 9 AM)
0 9 * * 1-5 SALTARE_API_KEY=sk_sal_... /usr/bin/python3 /path/to/task_reminder.py

Or use a Saltare automation skill to run it from within the workspace — no external server needed. See the Weekly Status Report recipe for the automation skill approach.

Error handling

The API returns standard HTTP status codes:

Code Meaning What to do
401 Invalid or missing token Check your API key
403 Key lacks the required scope Add the missing scope in Settings
404 Resource not found Verify the slug or ID
422 Validation error Check the error.message field for details
429 Rate limited Back off and retry after a few seconds

Rate limits are 600 requests/minute per key — more than enough for a periodic bot. If you're doing bulk operations, add a short sleep between pages.

Next steps

  • Add webhook triggers — instead of polling on a cron, subscribe to task.created events and check due dates in real time. See React to Events with Webhooks.
  • Bind to an agent — create the API key with an agent binding so reminders appear as messages from your bot agent, not your personal account.
  • Read the full reference — the REST API docs cover every endpoint, parameter, and error code.
  • Trigger agent runsPOST /api/v1/agents/:slug/message queues an agent response, letting you kick off AI workflows from external systems.

Tips

  • One key per integration. Revoking one shouldn't break the others.
  • Use narrow scopes. This bot only needs tasks:read, messages:write, and channels:read. Don't give it *.
  • Set an expiry on temporary keys. If you're just experimenting, set a 7-day expiry so you don't leave stale keys around.
  • Check the OpenAPI spec. GET /api/v1/openapi.json returns the machine-readable spec — drop it into Postman or use it for client code generation.