Countdown Timer API endpoint reference
Every route in the API, what it does, and what it costs you. All paths are relative to https://countdownshare.com/api/v1 and every response is JSON. If you want the machine-readable version instead, the OpenAPI 3.1 spec is public and needs no key.
Timers
The core resource. Sixteen routes, of which you will use three or four in a typical integration.
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /timers | List timers, filtered by type and status |
| POST | /timers | Create a timer — requires Idempotency-Key |
| GET | /timers/{timer_id} | Get one timer and its publication status |
| PATCH | /timers/{timer_id} | Update a timer — requires If-Match |
| DELETE | /timers/{timer_id} | Delete permanently |
| POST | /timers/{timer_id}/duplicate | Copy a timer — requires Idempotency-Key |
| POST | /timers/{timer_id}/archive | Move out of the active set |
| POST | /timers/{timer_id}/restore | Bring an archived timer back |
| POST | /timers/{timer_id}/actions | Start, pause, resume, or reset a duration timer |
| GET | /timers/{timer_id}/status | Read live status and remaining time |
| GET | /timers/{timer_id}/outputs | Get hosted page, embed, and email GIF URLs |
| GET | /timers/{timer_id}/activity | Paginated history of changes and actions |
| GET | /timers/{timer_id}/metrics | Views, unique visitors, interactions, conversions |
Webhooks
Destinations are account-level and reusable. Rules live under the timer they watch. Deliveries are the record of what we actually sent.
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /webhook-destinations | List destinations |
| POST | /webhook-destinations | Register a destination — returns the signing secret once |
| GET | /webhook-destinations/{destination_id} | Get one destination, without its secret |
| PATCH | /webhook-destinations/{destination_id} | Change name, URL, description, or enabled state |
| DELETE | /webhook-destinations/{destination_id} | Delete a destination no rule depends on |
| POST | /webhook-destinations/{destination_id}/test | Send a signed test delivery |
| POST | /webhook-destinations/{destination_id}/rotate-secret | Replace the signing secret |
| GET | /timers/{timer_id}/webhook-rules | List rules on a timer |
| POST | /timers/{timer_id}/webhook-rules | Attach a rule to a timer |
| PATCH | /timers/{timer_id}/webhook-rules/{rule_id} | Change a condition, destination, or state |
| DELETE | /timers/{timer_id}/webhook-rules/{rule_id} | Remove a rule |
| GET | /webhook-deliveries | List deliveries, filterable by status |
| GET | /webhook-deliveries/{delivery_id} | Get one delivery with its attempt history |
| POST | /webhook-deliveries/{delivery_id}/replay | Replay a delivery for the same event |
Account and reference
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /timezones | List accepted IANA timezone identifiers |
| GET | /usage | Current plan usage, limits, and cycle dates |
| GET | /usage/requests | Your API request log |
GET /usage is the cheapest way to confirm a key works and to see which environment it belongs to. It creates nothing and returns immediately.What counts against your allowance
Only creation is metered. Requests are not the billing unit — there is no monthly cap on API calls on any plan, just a per-minute rate limit.
Consumes a timer
POST /timersPOST /timers/{timer_id}/duplicate
Free, unlimited
Everything else. Reading status, updating a deadline, pausing, archiving, restoring, deleting, attaching rules, and reading metrics all cost nothing beyond a request against the per-minute limit.
Routes with extra header requirements
Beyond Authorization on everything and Content-Type on bodies, three routes require one more header — and reject the request without it.
| Route | Header | Value |
|---|---|---|
POST /timers | Idempotency-Key | Any stable string, 1–255 characters |
POST /timers/{id}/duplicate | Idempotency-Key | Any stable string, 1–255 characters |
PATCH /timers/{id} | If-Match | The timer's current revision, as an integer |
The timer object
What GET /timers/{timer_id} returns under data. Fields that do not apply to a timer's type are null rather than absent, so the shape is stable across all four types.
200 OK — a published fixed countdown
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Product launch",
"type": "fixed",
"status": "published",
"revision": 3,
"deadline_at": "2030-01-01T14:00:00.000Z",
"duration_seconds": null,
"remaining_seconds": 2692800,
"timezone": "America/New_York",
"recurrence": null,
"external_user_id": null,
"expiry": { "behavior": "show_message", "message": "This offer has closed" },
"metadata": { "campaign_id": "q1-launch" },
"outputs": {
"public_page_url": "https://countdownshare.com/api/c/550e8400-...",
"website_embed_html": "<iframe ...></iframe>",
"email_embed_html": "<a href=\"...\"><img src=\"...\"></a>",
"json_data_url": "https://countdownshare.com/api/v1/timers/550e8400-.../status"
},
"created_at": "2029-12-01T10:00:00.000Z",
"updated_at": "2029-12-14T08:31:22.000Z"
}Field reference
| Field | Type | Notes |
|---|---|---|
id | uuid | Immutable. The identifier every other endpoint takes. |
name | string | Your internal label, 1–200 characters. |
type | enum | fixed · duration · recurring · personalized. Fixed at creation. |
status | enum | draft · published · archived. Publication state, not the clock. |
revision | integer | Increments on every change. Send it in If-Match to update. |
deadline_at | string | null | ISO 8601 UTC. Non-null for fixed timers. |
duration_seconds | integer | null | Non-null for duration and personalized timers. |
remaining_seconds | integer | null | A snapshot at read time. Use /status for the live clock. |
timezone | string | IANA identifier. Defaults to UTC. |
recurrence | object | null | The schedule, for recurring timers. |
external_user_id | string | null | Your identifier, for personalized timers. |
expiry | object | behavior is show_message, hide, or redirect. |
metadata | object | Up to 50 of your own key-value pairs, returned verbatim. |
outputs | object | null | Display URLs. Null unless the timer is published in Production. |
created_at | string | ISO 8601 UTC. |
updated_at | string | ISO 8601 UTC. |
remaining_seconds on the timer object is a convenience snapshot taken when you read it. For anything you render, call the status endpoint — it returns a broken-down remaining time and the server clock you should be counting against.