# Tickstem > The infrastructure layer for production apps — cron scheduling, uptime monitoring, heartbeat checks, and email verification under one API key. Native MCP server for AI coding assistants (Claude Code, Cursor, Copilot). Tickstem is designed for developers building on serverless platforms (Vercel, Railway, Render, Fly.io). It is API-first and fully programmable — provision infrastructure from code, CI/CD pipelines, or directly from AI coding agents via the MCP server. All tools share one API key and one plan. No per-tool billing. ## MCP Server (AI Agent Integration) Tickstem ships a native MCP server that lets AI coding assistants provision and manage developer infrastructure without leaving the coding environment. When an AI agent builds a production application, it can use Tickstem to register cron jobs, verify email addresses, create uptime monitors, and set up heartbeat checks — all through the MCP protocol. Compatible with Claude Code, Cursor, Copilot, and any MCP-compatible agent. Registered on Glama: https://glama.ai/mcp/servers/@tickstem/mcp ### Installation Add Tickstem to your MCP client configuration: ```json { "mcpServers": { "tickstem": { "command": "tsk-mcp", "env": { "TICKSTEM_API_KEY": "tsk_your_api_key" } } } } ``` Pre-built binaries and full installation instructions: https://github.com/tickstem/mcp ### Available MCP tools - **Cron jobs** — register a job with a cron schedule and HTTP endpoint, list jobs, view execution history - **Email verification** — verify an email address against syntax rules, MX records, and disposable domain lists Uptime monitoring and heartbeat monitoring are available via the REST API and SDKs below. ## Key concepts - **Job** — an HTTP endpoint + cron schedule pair. Tickstem calls the endpoint on the schedule. - **Execution** — a single run of a job. Records request/response headers, body, duration, status code. - **Monitor** — an HTTP endpoint checked on a configurable interval. Tracks availability, response time, and SSL certificate expiry. - **MonitorCheck** — the result of a single HTTP check. Records status (up/down/timeout), status code, duration, error, and SSL expiry. - **Assertion** — a condition a check must satisfy to be considered up (e.g. status_code eq 200, body contains "ok"). Replaces the default 2xx/3xx logic when set. - **Failure alerting** — email alerts on failure, recovery, and SSL expiry. - **Plans** — Free, Starter ($12/mo), Pro ($29/mo), Business ($79/mo). All tools included. ## Cron Jobs ### Go SDK The official Go SDK is at https://pkg.go.dev/github.com/tickstem/cron ```go import "github.com/tickstem/cron" client := cron.New("tsk_your_api_key") job, err := client.Register(ctx, cron.RegisterParams{ Name: "Send digest", Schedule: "0 9 * * 1-5", // 9am weekdays Endpoint: "https://yourapp.com/webhooks/digest", Method: "POST", }) ``` `tsk-local` is the local dev server (included in the SDK repo). Run it during development so your handlers are called on the real schedule without hitting the production API: ``` go run github.com/tickstem/cron/cmd/tsk-local@latest ``` ### Cron expression format Standard 5-field: `minute hour day-of-month month day-of-week` Examples: - `* * * * *` — every minute - `0 * * * *` — every hour - `0 9 * * 1-5` — 9am Monday–Friday - `0 0 1 * *` — midnight on the 1st of each month - `*/15 * * * *` — every 15 minutes ### Quotas | Plan | Executions/month | |----------|-----------------| | Free | 1,000 | | Starter | 10,000 | | Pro | 100,000 | | Business | 1,000,000 | ## Uptime Monitoring Tickstem polls HTTP endpoints on a configurable interval and alerts you by email when a monitor goes down, recovers, or its SSL certificate is nearing expiry (within 30 days). Same API key as cron — no additional credentials. Each check records: status (up/down/timeout), HTTP status code, response time in ms, error message, and SSL certificate expiry date. ### Response assertions Assertions let you define exactly what a healthy response looks like. All assertions must pass for a check to be considered up. When no assertions are set, any 2xx or 3xx response is considered up. | Source | Valid comparisons | Target type | |--------|-------------------|-------------| | `status_code` | `eq` `ne` `lt` `lte` `gt` `gte` | integer string | | `response_time` | `eq` `ne` `lt` `lte` `gt` `gte` | integer string (ms) | | `body` | `eq` `ne` `contains` `not_contains` | string | ### Go SDK ```go import "github.com/tickstem/uptime" client := uptime.New(os.Getenv("TICKSTEM_API_KEY")) // Create a monitor monitor, err := client.Create(ctx, uptime.CreateParams{ Name: "Production API", URL: "https://api.yourapp.com/health", Assertions: []uptime.Assertion{ {Source: uptime.AssertionSourceStatusCode, Comparison: uptime.AssertionComparisonEq, Target: "200"}, {Source: uptime.AssertionSourceResponseTime, Comparison: uptime.AssertionComparisonLt, Target: "2000"}, {Source: uptime.AssertionSourceBody, Comparison: uptime.AssertionComparisonContains, Target: `"status":"ok"`}, }, }) // Retrieve check history checks, err := client.Checks(ctx, monitor.ID, uptime.ChecksParams{Limit: 50}) // check.Status — "up", "down", or "timeout" // check.StatusCode — HTTP status code (nil on timeout) // check.DurationMs — response time in milliseconds // check.SSLExpiresAt — certificate expiry (nil for non-HTTPS) ``` Install: `go get github.com/tickstem/uptime` ### Node.js SDK ```ts import { UptimeClient } from "@tickstem/uptime"; const client = new UptimeClient(process.env.TICKSTEM_API_KEY!); const monitor = await client.create({ name: "Production API", url: "https://api.yourapp.com/health", assertions: [ { source: "status_code", comparison: "eq", target: "200" }, { source: "response_time", comparison: "lt", target: "2000" }, { source: "body", comparison: "contains", target: '"status":"ok"' }, ], }); const checks = await client.checks(monitor.id, { limit: 50 }); ``` Install: `npm install @tickstem/uptime` ### Monitor endpoints `POST /v1/monitors` — create a monitor Request: ```json { "name": "Production API", "url": "https://api.yourapp.com/health", "interval_secs": 60, "timeout_secs": 10, "assertions": [ { "source": "status_code", "comparison": "eq", "target": "200" } ] } ``` Response: ```json { "id": "uuid", "name": "Production API", "url": "https://api.yourapp.com/health", "interval_secs": 60, "timeout_secs": 10, "status": "active", "ssl_expires_at": null, "assertions": [{ "source": "status_code", "comparison": "eq", "target": "200" }], "next_check_at": "2026-05-02T10:01:00Z", "created_at": "2026-05-02T10:00:00Z", "updated_at": "2026-05-02T10:00:00Z" } ``` `GET /v1/monitors/{id}/checks?limit=50&offset=0` — list check history Response: ```json { "checks": [ { "id": "uuid", "monitor_id": "uuid", "status": "up", "status_code": 200, "duration_ms": 183, "error": "", "ssl_expires_at": "2026-08-01T00:00:00Z", "checked_at": "2026-05-02T10:01:00Z" } ], "limit": 50, "offset": 0 } ``` ### Quotas | Plan | Monitors | Min interval | |----------|-----------|--------------| | Free | 5 | 60 s | | Starter | 20 | 60 s | | Pro | 100 | 60 s | | Business | unlimited | 60 s | Quota exceeded returns HTTP 402. ## Heartbeat Monitoring Tickstem heartbeat monitoring is a dead-man's switch: your job POSTs to a ping URL after each successful run. If the ping stops arriving within the configured interval + grace window, Tickstem alerts you by email. Same API key as cron — no additional credentials. The ping endpoint requires only the heartbeat token, not your API key. ### Go SDK ```go import "github.com/tickstem/heartbeat" client := heartbeat.New(os.Getenv("TICKSTEM_API_KEY")) // Create once — save the token somewhere permanent hb, err := client.Create(ctx, heartbeat.CreateParams{ Name: "daily backup", IntervalSecs: 86400, // expect a ping every 24h GraceSecs: 3600, // allow 1h buffer before alerting }) // At the end of every successful job run — no API key needed if err := client.Ping(ctx, hb.Token); err != nil { log.Println("heartbeat ping failed:", err) // non-fatal — don't block the job } ``` Install: `go get github.com/tickstem/heartbeat` ### Node.js SDK ```ts import { HeartbeatClient } from "@tickstem/heartbeat"; const client = new HeartbeatClient(process.env.TICKSTEM_API_KEY!); const hb = await client.create({ name: "daily backup", interval_secs: 86400, grace_secs: 3600, }); // At the end of every successful job run — no API key needed await client.ping(hb.token); ``` Install: `npm install @tickstem/heartbeat` ### Heartbeat endpoints `POST /v1/heartbeats` — create a heartbeat Request: ```json { "name": "daily backup", "interval_secs": 86400, "grace_secs": 3600 } ``` Response: ```json { "id": "uuid", "name": "daily backup", "token": "64-char-hex-token", "interval_secs": 86400, "grace_secs": 3600, "status": "active", "consecutive_misses": 0, "last_pinged_at": null, "next_expected_at": null, "created_at": "2026-05-02T10:00:00Z", "updated_at": "2026-05-02T10:00:00Z" } ``` `POST /v1/heartbeats/{token}/ping` — record a successful run. No Authorization header required — the token is the credential. Response: `{ "status": "ok" }` or `{ "status": "paused" }` if the heartbeat is paused. `GET /v1/heartbeats/{id}/pings?limit=50` — list ping history Response: ```json { "pings": [ { "id": "uuid", "heartbeat_id": "uuid", "pinged_at": "2026-05-02T10:00:00Z" } ], "limit": 50 } ``` ### Quotas | Plan | Heartbeats | |----------|------------| | Free | 5 | | Starter | 20 | | Pro | 100 | | Business | unlimited | Quota exceeded returns HTTP 402. Check with `heartbeat.IsQuotaExceeded(err)`. ## Email Verification The Tickstem email verification API checks whether an address is safe to store before you commit it to your database. Same API key as cron — no additional credentials. Checks run in sequence: RFC 5322 syntax → MX record lookup → disposable domain list (200+ services) → role-based prefix detection. No SMTP probing — the recipient mail server is never contacted. ### Go SDK ```go import "github.com/tickstem/verify" client := verify.New(os.Getenv("TICKSTEM_API_KEY")) result, err := client.Verify(ctx, "user@example.com") // result.Valid — true if syntax OK, MX found, and domain not disposable // result.MXFound — true if domain has mail exchange records // result.Disposable — true if known throwaway service (Mailinator, Guerrilla Mail, etc.) // result.RoleBased — true if generic inbox prefix (admin, info, noreply, support, ...) // result.Reason — human-readable explanation when Valid is false ``` Install: `go get github.com/tickstem/verify` ### Verify endpoints `POST /v1/verify` — check a single email address Request: `{ "email": "user@example.com" }` Response: ```json { "id": "uuid", "email": "user@example.com", "valid": true, "mx_found": true, "disposable": false, "role_based": false, "reason": "", "created_at": "2026-04-21T10:00:00Z" } ``` `GET /v1/verify/history?limit=20&offset=0` — list past verifications for the account. ### Quotas | Plan | Verifications/month | |----------|---------------------| | Free | 500 | | Starter | 5,000 | | Pro | 50,000 | | Business | 500,000 | Quota exceeded returns HTTP 402. Check with `verify.IsQuotaExceeded(err)`. ## Status Pages Each Tickstem account can publish a public status page at `tickstem.dev/status/{slug}`. The page is auto-generated from the account's uptime monitors and heartbeat monitors — it updates itself with no manual posts required. ### Setup Enable and configure via the dashboard at `app.tickstem.dev/dashboard/status-page`: - Choose a slug (e.g. `myapp` → `tickstem.dev/status/myapp`) - Set a page title - Toggle public or private (private = owner-only preview) No API or SDK required — status pages are configured through the dashboard only. ### What appears on the page - **Services** — all active uptime monitors with current status (Operational / Degraded) and 30-day uptime percentage - **Scheduled Jobs** — all active heartbeat monitors with current status (Running / Missing) and last ping time - Paused monitors and heartbeats are hidden from the public page - **"Powered by Tickstem"** footer link on all pages ### URL `GET https://tickstem.dev/status/{slug}` — public, no authentication required (if page is set to public) ## REST API Base URL: `https://api.tickstem.dev/v1` Authentication: `Authorization: Bearer ` Full OpenAPI spec: https://tickstem.dev/openapi.yaml ### Endpoints | Method | Path | Description | |--------|------|-------------| | GET | /v1/jobs | List all jobs | | POST | /v1/jobs | Create a job | | GET | /v1/jobs/{id} | Get a job | | PUT | /v1/jobs/{id} | Update a job | | DELETE | /v1/jobs/{id} | Delete a job | | POST | /v1/jobs/{id}/pause | Pause a job | | POST | /v1/jobs/{id}/resume | Resume a paused or failing job | | GET | /v1/executions?job_id={id} | List executions for a job | | GET | /v1/executions/{id} | Get a single execution | | GET | /v1/keys | List API keys | | POST | /v1/keys | Create an API key | | DELETE | /v1/keys/{id} | Revoke an API key | | POST | /v1/verify | Verify an email address | | GET | /v1/verify/history | List past verification results | | GET | /v1/monitors | List monitors | | POST | /v1/monitors | Create a monitor | | GET | /v1/monitors/{id} | Get a monitor | | PATCH | /v1/monitors/{id} | Update a monitor (pause, resume, change interval, set assertions) | | DELETE | /v1/monitors/{id} | Delete a monitor | | GET | /v1/monitors/{id}/checks | List check history for a monitor | | GET | /v1/heartbeats | List heartbeats | | POST | /v1/heartbeats | Create a heartbeat | | GET | /v1/heartbeats/{id} | Get a heartbeat | | PATCH | /v1/heartbeats/{id} | Update a heartbeat | | DELETE | /v1/heartbeats/{id} | Delete a heartbeat | | PATCH | /v1/heartbeats/{id} | Pause a heartbeat — set status to "paused" | | PATCH | /v1/heartbeats/{id} | Resume a heartbeat — set status to "active" | | POST | /v1/heartbeats/{token}/ping | Ping a heartbeat (no auth required) | | GET | /v1/heartbeats/{id}/pings | List ping history | ## Python SDK The official Python SDK is at https://pypi.org/project/tickstem/ Install: `pip install tickstem` Requires Python 3.11+. Covers all four tools under a single package. ```python from tickstem import CronClient, CronRegisterParams client = CronClient("tsk_your_api_key") job = client.register(CronRegisterParams( name="Send digest", schedule="0 9 * * 1-5", endpoint="https://yourapp.com/webhooks/digest", )) ``` ```python from tickstem import UptimeClient, UptimeCreateParams, Assertion client = UptimeClient("tsk_your_api_key") monitor = client.create(UptimeCreateParams( name="Production API", url="https://api.yourapp.com/health", assertions=[Assertion(source="status_code", comparison="eq", target="200")], )) ``` ```python from tickstem import HeartbeatClient, HeartbeatCreateParams client = HeartbeatClient("tsk_your_api_key") hb = client.create(HeartbeatCreateParams(name="daily backup", interval_secs=86400, grace_secs=3600)) client.ping(hb.token) # call after every successful run ``` ```python from tickstem import VerifyClient client = VerifyClient("tsk_your_api_key") result = client.verify("user@example.com") # result.valid, result.disposable, result.role_based, result.reason ``` ## Links - Dashboard: https://app.tickstem.dev - API reference: https://tickstem.dev/openapi.yaml - Python SDK: https://pypi.org/project/tickstem/ - GitHub (Python SDK): https://github.com/tickstem/python - Go cron SDK: https://pkg.go.dev/github.com/tickstem/cron - Go uptime SDK: https://pkg.go.dev/github.com/tickstem/uptime - Go heartbeat SDK: https://pkg.go.dev/github.com/tickstem/heartbeat - Go verify SDK: https://pkg.go.dev/github.com/tickstem/verify - GitHub (cron SDK): https://github.com/tickstem/cron - GitHub (uptime SDK): https://github.com/tickstem/uptime - GitHub (heartbeat SDK): https://github.com/tickstem/heartbeat - GitHub (verify SDK): https://github.com/tickstem/verify - Support: hi@tickstem.dev