Meerkat — open source webhook-native agent task API

Meerkat mascot
Describe a task in plain English. Meerkat runs it async — results POST to your webhook.

meerkat-agents

Official Ruby client for Meerkat — an open source, webhook-native API for async agent tasks.

Install as meerkat-agents, require as meerkat:

gem "meerkat-agents"
# ...
require "meerkat"

Gem Version License: MIT


About Meerkat

Meerkat is open source infrastructure for async agent tasks. You describe work in plain English, connect your own LLM key (BYOK), and Meerkat schedules execution and POSTs signed JSON to your webhook — without building schedulers, scrapers, or retry logic.

Resource Link
Website meerkatagents.com
Documentation meerkatagents.com/docs
Use cases Package tracking · Site monitoring · Agent webhooks
Developer guides meerkatagents.com/blog
Sign up (Cloud) cloud.meerkatagents.com/signup
Open source server github.com/Tiny-Bubble-Company/meerkat

This repo is the official Ruby gem (meerkat-agents on RubyGems). For product overview, use cases, and self-host vs Cloud, read more on the Meerkat website.

Other SDKs: meerkat-python · meerkat-javascript


Table of contents


What is Meerkat?

Meerkat gives developers a single primitive for agentic async work:

Register a task  Meerkat runs it  findings POST to your webhook

Describe a task in plain English, pass structured input_params, bring your own LLM key (BYOK), and Meerkat's agent executes the work — fetching webpages, extracting structured findings, and delivering JSON to your output_webhook on a schedule or on demand.

No polling. No per-carrier SDKs. No LLM tool loops to maintain.

This gem wraps the Meerkat REST API so you can create tasks, trigger runs, and verify inbound webhooks from Ruby and Rails apps without hand-rolling HTTP clients.


What problem does it solve?

Building async agent workflows usually means stitching together:

  • A job queue and scheduler
  • LLM calls with tool use (fetch URL, parse HTML, compare state)
  • Webhook delivery and retry logic
  • Change detection between runs

Meerkat handles all of that as a managed API. meerkat-agents handles the client side:

Without the gem With meerkat-agents
Raw curl / Faraday calls Typed resource methods (client.tasks.create, client.tasks.run)
Manual auth headers API key configured once
DIY webhook HMAC verification Meerkat::Webhooks.verify + Rails concern
Error parsing by hand Meerkat::NotFoundError, ValidationError, etc.

Why use this gem?

  • Webhook-native — every task delivers JSON to your endpoint; the gem helps you verify signatures
  • BYOK — Anthropic, OpenAI, OpenRouter, or Grok; keys stored on your Meerkat account
  • Recurring + one-off — natural-language schedules (every 2 hours) or ad-hoc runs
  • Self-host or Cloud — same API against Docker, Render, Fly.io, or Meerkat Cloud
  • Rails-ready — optional railtie, env-based config, webhook verification concern

How it works

┌─────────────┐     client.tasks.create   ┌─────────────┐     agent + tools    ┌─────────────┐
│  Your app   │ ───────────────────────►  │   Meerkat   │ ──────────────────►  │  Web / APIs │
└─────────────┘                           └─────────────┘                      └─────────────┘
       ▲                                          │
       │              POST output_webhook         │
       └──────────────────────────────────────────┘
              (verify with Meerkat::Webhooks)
Step What happens
1. Register client.tasks.create(...) — describe work, pass params, set webhook
2. Run Meerkat schedules recurring tasks or runs one-offs on demand
3. Receive Findings POSTed to your webhook as structured JSON

Use cases

What teams ship with Meerkat — aligned with the use cases on meerkatagents.com:

Use case In one line On the website
Package tracking Webhook when DHL, UPS, FedEx, or any carrier status changes Package tracking →
Website monitoring Watch a URL; get notified when the signal you describe happens Site monitoring →
Price & stock Competitor price drops and restocks without building a scraper Guide →
Async agent webhooks Register LLM agent work; results POST to your endpoint Agent webhooks →

Read the full developer guides for step-by-step tutorials with API examples.


Package tracking — webhook when status changes

Monitor any courier tracking link — DHL, UPS, FedEx, USPS, DPD, Royal Mail — with one API. Meerkat's agent reads the carrier page on a schedule, detects state changes, and POSTs structured JSON to your endpoint. No per-carrier SDKs, no polling loops, no LLM tool code to maintain.

  • Every carrier, one endpoint — pass a courier_tracking_link; adding a new carrier is zero integration code.
  • Webhook on change only — hear from Meerkat when status moves (In transit → Out for delivery → Delivered), not on every poll.
  • Signed and retried — HMAC-SHA256 signatures, exponential backoff, delivery history via the events API.
client.tasks.create(
  task_type: "recurring",
  description: "Monitor DHL tracking and report status changes",
  input_params: { courier_tracking_link: "https://www.dhl.de/track?id=..." },
  frequency: "every 2 hours",
  output_webhook: "https://your-app.com/webhooks/meerkat"
)

Package tracking use case · Shipment tracking guide


Website monitoring — watch any URL

A change-detection API that understands intent, not just pixel diffs. Describe the signal in plain English — price under $99, plan name changed, status page updated — and Meerkat watches on a schedule. It only fires your webhook when that condition is met.

  • Semantic, not cosmetic diff — no CSS selectors to maintain when the page layout shifts.
  • JS-rendered pages — SPAs and dynamic content via headless fetch before reasoning.
  • Your schedule — natural language (every 15 minutes), cron, or on-demand client.tasks.run(id).
client.tasks.create(
  task_type: "recurring",
  description: "Notify me when this product drops below $99",
  input_params: { url: "https://shop.example.com/widget-pro" },
  frequency: "every 30 minutes",
  output_webhook: "https://your-app.com/webhooks/meerkat"
)

Website monitoring use case · Monitor website for changes guide


Price & stock monitoring — competitor alerts

Track competitor product pages and get a signed webhook when price drops, price increases, or stock flips (out of stock → in stock). One task per SKU — no scraper fleet, scheduler, or retry layer to operate.

client.tasks.create(
  task_type: "recurring",
  description: "Report price and stock whenever either changes",
  input_params: { url: "https://competitor.com/product/sku-123" },
  frequency: "every 15 minutes",
  output_webhook: "https://your-app.com/webhooks/meerkat"
)

Example webhook when price moves:

{
  "data": {
    "summary": "Price dropped from 99.00 to 89.00",
    "findings": { "price": 89.0, "previous_price": 99.0, "in_stock": true },
    "change_detected": true
  }
}

Competitor price & stock guide


Async agent webhooks — skip the scheduler boilerplate

Meerkat is the open source primitive for engineers who would rather ship product than rebuild schedulers, LLM tool loops, retries, and signed webhook delivery. Register a task in plain English, attach your LLM key (BYOK), point at an endpoint — done.

  • One verb: registerPOST /tasks creates recurring or one-off agent work.
  • Webhook-native results — structured JSON, signed, retried until 2xx (same contract as Stripe or GitHub webhooks).
  • MIT & self-hostable — Docker, Render, Fly.io, or Meerkat Cloud; identical REST API.
client.tasks.create(
  task_type: "recurring",
  description: "Summarize new arXiv papers in distributed systems",
  input_params: { topic: "distributed systems" },
  frequency: "0 9 * * *",
  output_webhook: "https://your-app.com/webhooks/meerkat"
)

Agent webhooks use case · Webhook vs polling guide


One-off lookups

Ad-hoc agent tasks without standing up a schedule — research, single URL extraction, or on-demand runs triggered from your app:

client.tasks.create(
  task_type: "one_off",
  description: "Fetch the current price of this product once",
  input_params: { url: "https://example.com/product/123" },
  output_webhook: "https://your-app.com/webhooks/meerkat"
)

Scheduled scraping with webhooks

Need data on a cadence, not just change detection? Set a frequency and receive structured findings every run — see the web scraping API guide.


Task types

Type Behavior Best for
recurring Runs on a schedule; compares state between runs; reports changes Courier tracking, uptime, price watches
one_off Runs once, reports findings, marks task complete Ad-hoc research, single lookups

Frequency accepts natural language (every 30 minutes, hourly, every 2 hours) or cron expressions. Required for recurring tasks; omit for one_off.


Self-host vs Meerkat Cloud

Self-host (OSS) Meerkat Cloud
API Identical Identical
LLM keys You supply (BYOK) You supply (BYOK)
Infra / ops You run Postgres + workers Managed for you
Base URL http://localhost:3000/api/v1 https://cloud.meerkatagents.com/api/v1 (default)

LLM usage is always billed by your provider. Meerkat never sees your model costs.

Self-host: meerkat README — Docker


Installation

Add to your Gemfile:

gem "meerkat-agents"

Or install directly:

gem install meerkat-agents

Then in your app:

require "meerkat"

Quick start

1. Get an API key

Sign up via the Meerkat Cloud dashboard or programmatically:

require "meerkat"

client = Meerkat::Client.new(base_url: "https://cloud.meerkatagents.com/api/v1")
result = client..create(email: "you@company.com", name: "Your Name")
api_key = result.dig("data", "api_key") # store securely — shown once

2. Create a task

client = Meerkat::Client.new(api_key: ENV["MEERKAT_API_KEY"])

response = client.tasks.create(
  task_type: "recurring",
  description: "Monitor courier tracking and notify on status changes",
  input_params: { courier_tracking_link: "https://..." },
  frequency: "every 2 hours",
  output_webhook: "https://your-app.com/webhooks/meerkat"
)

task = response["data"]

3. Trigger a run (optional)

client.tasks.run(task["id"], async: true)

Examples

List and filter tasks

client.tasks.list(status: "active", task_type: "recurring", limit: 20)
client.tasks.retrieve(1)
client.tasks.pause(1)
client.tasks.resume(1)
client.tasks.delete(1)

One-off lookup

client.tasks.create(
  task_type: "one_off",
  description: "Fetch the current price of this product",
  input_params: { url: "https://example.com/product/123" },
  output_webhook: "https://your-app.com/webhooks/meerkat"
)

Inspect runs and webhook delivery history

client.tasks.runs(1, limit: 10)
client.tasks.events(1, limit: 50)

API keys

client.api_keys.list
client.api_keys.create(name: "Production")
client.api_keys.revoke(key_id)

Receiving webhooks (Rails)

Every outbound POST is HMAC-SHA256 signed in the X-Meerkat-Signature header. Verify before processing:

Meerkat::Webhooks.verify(
  payload: request.raw_post,
  signature: request.headers["X-Meerkat-Signature"],
  secret: ENV["MEERKAT_WEBHOOK_SECRET"]
)

Rails controller concern

class Webhooks::MeerkatController < ApplicationController
  include Meerkat::Rails::WebhookVerification

  def create
    payload = JSON.parse(request.raw_post)
    # handle event: run_completed, status_changed, etc.
    head :ok
  end
end

Set MEERKAT_WEBHOOK_SECRET in your environment.


Output formats

output_webhook is required on every task (or use "default" if your account has a default webhook configured). output_format is optional.

Preset Webhook payload shape
default { event, task_id, task_run_id, occurred_at, data: { summary, findings, change_detected, ... } }
compact Top-level summary, change_detected, findings
flat Findings merged into the webhook root object
findings_only Findings object only
minimal event, task_id, summary, change_detected
client.tasks.create(
  description: "...",
  input_params: { url: "https://..." },
  output_webhook: "https://your-app.com/hook",
  output_format: "flat"
)

Any other string is passed to the agent as a custom instruction for shaping findings (max 500 chars).


API reference

Default base URL: https://cloud.meerkatagents.com/api/v1

All endpoints except signup require Authorization: Bearer mk_....

SDK method HTTP Description
client.signup.create(...) POST /signup Create account + API key
client.api_keys.list GET /api_keys List API keys
client.api_keys.create(name:) POST /api_keys Generate a new key
client.api_keys.revoke(id) DELETE /api_keys/:id Revoke a key
client.tasks.list(...) GET /tasks List tasks
client.tasks.create(...) POST /tasks Create a task
client.tasks.retrieve(id) GET /tasks/:id Task details + last known state
client.tasks.update(id, ...) PATCH /tasks/:id Partial update
client.tasks.replace(id, ...) PUT /tasks/:id Full replace
client.tasks.delete(id) DELETE /tasks/:id Archive or delete
client.tasks.run(id, async:) POST /tasks/:id/run Trigger on-demand run
client.tasks.pause(id) POST /tasks/:id/pause Pause recurring task
client.tasks.resume(id) POST /tasks/:id/resume Resume recurring task
client.tasks.runs(id) GET /tasks/:id/runs Run history
client.tasks.events(id) GET /tasks/:id/events Webhook delivery log

Full OpenAPI spec: meerkat/openapi/openapi.yaml


Configuration

Environment variables

Variable Description
MEERKAT_API_KEY API key from signup (mk_...)
MEERKAT_BASE_URL API base URL (default: Meerkat Cloud)
MEERKAT_WEBHOOK_SECRET Secret for verifying inbound webhooks

Rails / global config

Meerkat.configure do |config|
  config.api_key = ENV["MEERKAT_API_KEY"]
  config.base_url = ENV.fetch("MEERKAT_BASE_URL", Meerkat::Client::DEFAULT_BASE_URL)
end

Meerkat.configuration.client.tasks.list(status: "active")

Self-hosted instance

client = Meerkat::Client.new(
  api_key: ENV["MEERKAT_API_KEY"],
  base_url: "http://localhost:3000/api/v1"
)

Development

bundle install
bundle exec rake spec

See PUBLISHING.md for local testing and RubyGems release steps.



License

MIT — see LICENSE.