# Server timers and private automation workspaces

A server timer continues after every browser tab closes. A stored deadline and a Cloudflare Durable Object alarm run the countdown; an optional Zapier Catch Hook receives completion. HTTP and MCP expose status and cancellation. No continuously running browser, polling loop or process is needed to make the deadline fire.

[Create or open a free workspace](https://eggtimer.app/automate.html). The ordinary browser timer remains account-free. This is a free plan with usage limits, with no payment details or email required. Optional email recovery is not configured yet.

## Private keys, simple access

POST https://eggtimer.app/api/workspaces with Content-Type: application/json and {"accept_limits":true}. This creates an isolated workspace and returns workspace_id, url, key and recovery_key once. Native/server clients may omit Origin; browser requests must be same-origin. Save both keys privately. No key belongs in a timer URL, recipe, source repository or screenshot.

The ew_ API key creates, reads and cancels only this workspace's timers. The er_ recovery key replaces the API key or closes the workspace; keep it out of Zapier and AI clients. The service stores credential hashes, not retrievable key copies. The web page holds credentials in memory, offers an explicit file download, and does not put keys in browser storage or cookies. A credential file is itself private: protect it like a password.

If you lose the API key, open Already have a workspace? and use the recovery key. If both credentials are lost, access cannot be recovered. Email recovery is not available. Authentication uses a workspace Bearer key. Configure it in an MCP client that supports Authorization headers.

## Free plan limits

- 50 new timers or routines per workspace per UTC day; 10 active timers or delivery retries at once.
- Each duration/deadline is one second to 30 days. Finite multi-step routines are available through the separate routine endpoint.
- Shared deployment allowance: 500 new workspace timers/day, 50 new workspaces/day, and ten registrations per network/day. Registration uses daily salted hashes for abuse prevention, not raw stored addresses.
- The edge permits 60 requests/minute/IP. Hosting quotas also apply; limits can cause failures. This free plan has no uptime or delivery-latency SLO.
- Up to 100 recent timer records appear in a workspace. Timer metadata expires seven days after completion, cancellation or terminal delivery. Workspace access expires after 90 days without use. Closing a workspace revokes its API key immediately and retains its remaining metadata for seven days.

Idempotent retries do not consume another timer allowance. A reserved create whose network outcome is uncertain can consume a slot/allowance; retry the same request key to recover its result. A full allowance blocks new timers; existing timers continue. Registration can be paused without changing the account-free browser timer. For finite repeated recipe stages and per-step events, use the [server routine API](https://eggtimer.app/server-routines.md). Unbounded calendar recurrence, server pause/resume, push to phones and native phone alarms are not implemented.

## Schedule a countdown or a deadline

Use Authorization: Bearer YOUR_WORKSPACE_API_KEY and Content-Type: application/json. The body is bounded at 2 KiB. POST https://eggtimer.app/api/server-timers:

```json
{"duration_seconds":1500,"title":"Focus","idempotency_key":"your-stable-source-event-id"}
```

For an exact instant, replace duration_seconds with deadline_at, an ISO date-time with an explicit UTC offset, e.g. 2030-01-01T17:00:00+07:00. Choose an instant one second to 30 days in the future; a past or too-distant new deadline is rejected. Do not send both fields. The web workspace also accepts local input such as 9pm, resolving it to an explicit instant on the visitor's device before scheduling.

Adding webhook_url authorizes automatic completion delivery to that private HTTPS hooks.zapier.com Catch Hook. Other destinations are not supported. max_attempts is 1–5, default 3. A hook can run downstream actions; use a dedicated test Zap first.

A new request returns HTTP 201 and {created:true,timer:...}. An identical retry returns HTTP 200 with the same ID and original deadline; conflicting reuse returns 409. Generate one idempotency_key per intentional timer: 10–80 letters, digits, hyphens or underscores. Reuse it for transport retries only. Records preserve idempotence through the timer and seven days after its terminal state.

## Read, link, list and cancel

GET https://eggtimer.app/api/workspace returns workspace identity, usage and limits. GET /api/server-timers lists this workspace's active/recent records. GET /api/server-timers/TIMER_ID returns one record. POST /api/server-timers/TIMER_ID/cancel cancels a countdown or a pending retry. An ID from another workspace returns 404. Repeated cancellation is safe. A delivery in progress returns 409 because it cannot be recalled; an accepted event cannot be undone.

Every timer returns its stable url: https://eggtimer.app/timers/TIMER_ID. Opening it requires the workspace's API key; the URL grants no access by itself. The authenticated workspace view highlights that record. The link survives label changes, but its record has the retention limit above. GET status is the machine-readable counterpart. Workspace url uses /workspaces/WORKSPACE_ID. See the [linking contract](https://eggtimer.app/linking.md).

Timer fields include id, url, status (scheduled/completed/cancelled), title, duration_seconds, created_at, deadline_at, remaining_seconds, completed_at, cancelled_at, delivery and expires_at. Times are ISO UTC. completed_at records when the server observed the deadline; it may be later than deadline_at. A zero remaining time can precede alarm processing. delivery has status (not_requested/pending/sending/accepted/failed/cancelled), attempts, max_attempts, last_http_status, next_attempt_at, accepted_at and last_error. Hook URLs and keys are never returned in timer status.

## Replace a key or close a workspace

Use the recovery key in the Bearer header, with {"confirm":true} as JSON.

- POST /api/workspace/rotate returns a new API key and revokes the old key. Existing timers and the recovery key remain valid. Update your saved file and reconnect clients.
- POST /api/workspace/close revokes API access, cancels pending timers and blocks future webhook attempts. Already in-flight/sent HTTP requests cannot be recalled. This operation cannot be reversed through the beta UI.

The recovery key cannot be used as the timer API key. These management operations are deliberately not MCP tools. A separate owner key remains supported for the original owner workspace; it is not a key to customer workspaces. The legacy owner endpoint has eight tools and no workspace list or public enrollment privileges.

## Delivery semantics

One Durable Object per timer isolates slow destinations. At completion it POSTs the version-1 timer.completed event: version, event, event_id, title, duration_seconds, completed_at and source. event_id is the stable timer ID. The event's completed_at is the scheduled deadline; status separately records observation and acceptance times.

HTTPS only, no redirects, 10-second timeout. Network errors/timeouts and HTTP 408/425/429/5xx can retry with backoff of 5s, 30s, 120s and 600s up to max_attempts. Other unsuccessful responses end delivery. A persisted claim and a 30-second recovery alarm cover process interruption. At-least-once attempts can duplicate effects; the same event_id and Idempotency-Key are reused, but a raw Zapier Catch Hook does not automatically deduplicate them. Add durable downstream deduplication before consequential actions. Even max_attempts:1 cannot establish exactly-once effects after an uncertain response.

accepted means HTTP success from the hook, not successful later Zap actions. failed can include an uncertain outcome: inspect Zap history before creating a replacement. Deadline delivery is best effort; outages, quotas and downstream failures can delay/prevent effects. There is no emergency-alert guarantee. Closing a workspace blocks new attempts; changing its API key does not cancel timers.

## MCP and native Zapier beta

Connect a header-capable client to https://eggtimer.app/api/mcp/server with Authorization: Bearer YOUR_WORKSPACE_API_KEY. Its 18 tools include seven public link/discovery tools, create_server_timer, get_server_timer, cancel_server_timer, get_workspace, list_server_timers, and six [routine/event tools](https://eggtimer.app/server-routines.md). For focused connections, append ?profile=timers (five tools) or ?profile=routines (seven tools). Profiles reduce tool-definition overhead and reject calls outside that selection; they do not narrow credential permissions. Creation starts a real server countdown immediately. Public /api/mcp keeps seven stateless tools and needs no key. Public create_timer only returns a reusable link; it starts no countdown.

The private native Zapier beta source is prepared separately from the site. It has authenticated duration/deadline creation, cancellation, lookup, two public link actions and a polling completion trigger. A polling trigger follows the Zapier plan's interval; it is not instant and can miss records that expire or fall outside recent history. For direct completion POSTs use a Catch Hook. Private registration/testing is separate from public App Directory review; the [private invitation and setup guide](https://eggtimer.app/zapier) is available now. Version 0.1.0 has hosted remote-invocation evidence for all operations; this does not establish enabled live Zap history or public approval.

## Storage, cost and operation

The workspace registry stores its ID, credential hashes, limits/usage and recent timer metadata. Pending hook URLs stay only in their timer's storage and are removed at terminal delivery or cancellation. Operational records are separate from anonymous browser-start aggregates. Ordinary idle workspaces expire after 90 days; the isolated workspace assigned to Zapier product reviewers has no idle expiry and no access to other workspaces; closed workspace metadata is purged after seven days. Application observability is disabled; providers still process normal network metadata. Avoid sensitive titles.

No paid hosting plan or billing system is enabled by this release. Cloudflare's Free limits can stop operations. A future paid plan has a base subscription and metered compute/storage; do not assume indefinite free capacity. Domain renewal and any Zapier subscription belong to their respective owners. Mailgun costs depend on the existing account's allowance; it is not sending recovery emails here yet.

The owner CLI uses credentials outside Git, with mode 0600. npm run server:setup -- --provision provisions the legacy owner key through stdin; npm run --silent server -- create reads JSON on stdin, status/cancel take an ID. For workspace use set EGGTIMER_SERVER_KEY securely to its API key. The explicit human-operated credentials command prints private MCP connection JSON; never run it into shared logs.

[Workspace](https://eggtimer.app/automate.html) · [AI/MCP](https://eggtimer.app/ai.html#mcp) · [OpenAPI](https://eggtimer.app/openapi.json) · [Linking](https://eggtimer.app/linking.md) · [Browser Zapier](https://eggtimer.app/webhooks.md) · [Privacy](https://eggtimer.app/privacy.md)
