# Easerix Docs — full corpus
> Documentation for Easerix — the unified business suite: tasks, notes, e-signature, time tracking, short links, and forms on one platform with one login.
# Getting started
Create an Easerix account, set up your workspace, and start using the tools — solo or with a team.
Canonical: https://easerix.com/docs/getting-started
Sign up once and every Easerix tool is yours: you get a personal workspace
automatically, and you can create an organization and invite your team
whenever you're ready. There is no separate setup per tool — one login, one
workspace, tools enabled per organization.
## Create your account
1. Go to [login.easerix.com](https://login.easerix.com) and sign up with your email — password or magic link, your choice.
2. You land in the Easerix app with a **personal workspace** already created. Nothing to configure.
3. Open any tool from the launcher. Your work is private to you until you share it.
## Work with a team
Solo first is the default — organizations are an upgrade, never a prerequisite.
1. Open [account.easerix.com](https://account.easerix.com) and choose **Create organization**.
2. Invite people by email, or share an invite link.
3. Assign roles: **owner**, **admin**, **billing**, or **member**. Admins can create **teams** inside the organization for finer grouping.
4. Control which tools the organization uses under **Tools** — every tool is on by default; switch off what you don't need.
Switching between your personal workspace and organizations happens from the workspace switcher — your personal space never goes away.
## Where everything lives
| Surface | URL |
|---|---|
| The app (all tools) | [app.easerix.com](https://app.easerix.com) |
| Account & organization settings | [account.easerix.com](https://account.easerix.com) |
| Sign in | [login.easerix.com](https://login.easerix.com) |
## Frequently asked questions
### Do I need a company account to use Easerix?
No. Every account starts with a personal workspace that works exactly like an organization of one. Create an organization only when you want to share work.
### Can I belong to more than one organization?
Yes. Accounts support multiple organization memberships, and you switch between them (and your personal workspace) without signing out.
### How does my team sign in?
Everyone signs in once at login.easerix.com; that session works across every Easerix tool — there are no per-tool logins.
# CronCrunch FAQ
Quick answers about timers, entries, billing, teams, preferences, and privacy in CronCrunch.
Canonical: https://easerix.com/docs/croncrunch/faq
The short version: one timer at a time, entries are always editable and always
yours, billable amounts come from each project's hourly rate, rounding is
display-only, sharing a project shares the project and never your entries,
and monitoring records nothing unless you switch it on. Details below.
## Timers and entries
### What happens if I forget to stop the timer?
It keeps running until you stop it — and a timer that runs unusually long can trigger a reminder notification. After stopping, just edit the entry's duration to the real figure.
### Can I edit an entry after the fact?
Yes — everything: description, project, duration, billable flag, and tags. Edit inline on **Time entries**, click an entry on the **Calendar**, or retype a cell on the **Timesheet**.
### How do I enter durations?
Three formats work everywhere: `1:30` (hours:minutes), `90` (minutes), and `1.5` (decimal hours) — all three mean ninety minutes.
### Can I log time for a past day?
Yes. Drag it onto the right day in the **Calendar**, or type into that day's cell on the **Timesheet**.
## Billing
### Where does the "Amount" in reports come from?
Billable hours × the **hourly rate** on each entry's project, shown in your chosen currency. Entries can be marked non-billable individually, and whole projects can be non-billable.
### Does rounding change my data?
No. **Settings → Preferences → Rounding** affects how reports display durations (nearest 5–30 minutes). Entries always keep exact times, and exports reflect the stored data.
### How do I invoice from CronCrunch?
Use **Reports → Export CSV** for the period — it includes date, times, project, client, description, tags, hours, and the billable flag per entry.
## Teams
### Can my team track time on the same project?
Yes — **Share** the project from its card on **Projects**. Teammates then see it and log their own hours against it.
### Can I see a teammate's hours?
No. Time entries are personal; a shared project shares the project definition, not anyone's entries. The **Everyone / Mine / Shared** lens on Projects filters projects, not people's time.
## Preferences
### Can weeks start on Sunday?
Yes — **Settings → Preferences → Week starts on** switches between Monday and Sunday, and the Timesheet, Calendar, and weekly reports all follow it.
### Which currencies are supported?
USD, EUR, GBP, CAD, AUD, and NGN, as a display preference in **Settings → Preferences**.
## Privacy
### Is CronCrunch watching what I do?
Not unless you switch it on. Activity and screenshot capture are off by default, per-user, server-enforced, and viewable only by you. The full, plain-language accounting is on the [Monitoring page](/croncrunch/monitoring).
## Where things run
| Surface | URL |
|---|---|
| CronCrunch | [app.easerix.com](https://app.easerix.com) → CronCrunch |
| Account & organization settings | [account.easerix.com](https://account.easerix.com) |
# CronCrunch
Time tracking — clients, projects with budgets, a running timer, timesheets, a week calendar, tags, reports, and opt-in monitoring.
Canonical: https://easerix.com/docs/croncrunch
CronCrunch is Easerix's time-tracking tool: organize work into clients and
projects, track with a live timer or log time by hand, fill a weekly
timesheet or drag entries onto a calendar, tag and bill entries, and read
reports of where your hours went and what they earned. Monitoring is strictly
opt-in.
## What you can do
| Capability | In short |
|---|---|
| Timer | One running timer — start on a project, stop when done |
| Time entries | Manual logging, inline editing, billable flag, delete |
| Tags | Label entries, create tags inline, filter by tag |
| Timesheet | Week grid per project — type hours straight into cells |
| Calendar | Week timeline — drag on empty space to log time |
| Clients & projects | Projects belong to clients, carry a color, hourly rate, and budget |
| Budgets | Per-project hour budgets with progress bars and over-budget warnings |
| Reports | Totals, billable amount, by-project/by-client and by-day breakdowns, CSV export |
| Shared projects | Share a project so teammates can log their own hours against it |
| Monitoring | Opt-in activity timeline and screenshots from the desktop app — [read the details](/croncrunch/monitoring) |
## How it fits together
**Clients → projects → time entries.** A client is who you bill; a project is what you track against (with its rate, budget, and color); a time entry is a stretch of tracked or logged time on a project. Everything else — timesheet, calendar, reports — is a different lens on the same entries.
Your entries are yours: sharing a project lets teammates log *their own* hours against it, and never exposes anyone's entries to anyone else.
## Next steps
- [Track your first hour](/croncrunch/quickstart)
- [Timesheets, tags, budgets, and reports](/croncrunch/timesheets-and-reports)
- [Monitoring — what it records and who sees it](/croncrunch/monitoring)
## Frequently asked questions
### Where do I open CronCrunch?
In the Easerix app at [app.easerix.com](https://app.easerix.com) — CronCrunch is one of the tools in the launcher.
### Can I run two timers at once?
No — there is exactly one running timer. Starting work on something else means stopping the current timer first, which keeps entries honest.
### Is anything recorded without my say-so?
No. Activity and screenshot capture are off by default, per-user, and the server refuses captures for any account that hasn't switched them on. The full picture is on the [Monitoring page](/croncrunch/monitoring).
# Monitoring — what is recorded, and who sees it
A plain accounting of CronCrunch's opt-in activity and screenshot capture — what's collected, when, who can view it, and how to turn it off.
Canonical: https://easerix.com/docs/croncrunch/monitoring
Monitoring is off by default and strictly opt-in, per person. When you enable
it, the CronCrunch desktop app can record which app and window title you have
focused, and take periodic screenshots. Only you can view your captures —
there is no manager or team view — and you can turn it off any time.
This page states exactly what monitoring does, based on how the product is built — not on what a sales page would like it to do.
## What is captured
Two independent channels, each with its own toggle on the **Monitoring** page:
| Channel | Exactly what's stored |
|---|---|
| **Record active app & window title** | The focused app's name, its window title, and the start/end time of that focus — nothing else |
| **Take periodic screenshots** | A screenshot image (up to 5 MB) and the time it was captured |
Either capture can be linked to the time entry it happened during, so it shows up in context.
**And what is not.** CronCrunch has no way to receive keystrokes, mouse movement, webcam or microphone recordings, or browsing history — the service simply has no endpoints for them. The two channels above are the entire surface.
## When capture happens
- **Off by default, for everyone.** A brand-new account has both channels disabled. Monitoring only starts if *you* switch it on.
- **Enforced by the server, not just the app.** When a channel is off for your account, the CronCrunch service rejects any capture sent for it. It's not a client-side setting that software could quietly ignore — the toggles are the enforcement boundary.
- **Capture comes from the desktop app.** The web app at app.easerix.com never records anything; it only shows your settings and what has been captured. To capture at all you need the CronCrunch desktop app running and signed in as you.
- **Screenshots run at the interval you choose** — every 2, 5, 10, or 15 minutes (5 by default).
- **Your consent is timestamped.** The first time you enable either channel, the moment is recorded and shown on the Monitoring page ("Consent recorded …").
## Who can view it
**Only you.** Every screenshot and activity event is stored against your own account, and every way of viewing them returns only your own. There is no manager view, no admin view, no teammate view, and no organization-wide export — those capabilities do not exist in the product today.
Sharing a project with your organization changes nothing here: shared projects let teammates log their own hours; they never expose your entries, your activity, or your screenshots.
Screenshot images live in private storage and are displayed through links that expire after ten minutes — there is no permanent public URL to a screenshot.
## How to turn it off — and what happens to the data
1. Open **Monitoring** in the CronCrunch sidebar.
2. Switch off either toggle (or both). From that moment the server rejects new captures on that channel.
For data that already exists:
| Data | Deleting it |
|---|---|
| Screenshots | Delete any screenshot individually from the Monitoring page — this removes both the record and the stored image, and cannot be undone |
| Activity events | There is currently **no way to delete individual activity events** in the app. Turning the toggle off stops new recording, but past events remain visible on your timeline |
## Reviewing your own captures
The **Monitoring** page shows your last 7 days in two tabs:
- **Screenshots** — thumbnails grouped by day, each with its capture time and a delete button.
- **Activity** — a per-day list of app + window title with the time and duration of each focus stretch.
## Frequently asked questions
### Can my employer require this and see my screen?
CronCrunch has no mechanism for that today. The settings are per-user and self-service, and captures are only viewable by the person they belong to. There is no admin override and no manager dashboard.
### Does monitoring only run while my timer is running?
The in-app description says the desktop app records only while your timer runs, with a visible indicator. What the server enforces is your toggles: with a channel enabled, it accepts that channel's captures regardless of timer state. Treat the toggles — not the timer — as the on/off switch that actually binds.
### If I delete a screenshot, is the image really gone?
Yes — deleting removes the database record and the stored image file. The Monitoring page asks for confirmation because it cannot be undone.
### Is anything captured from this web page or my browser?
No. Capture requires the desktop app. The web app only reads and manages what was captured.
# Track your first hour
Client → project → timer — from zero to tracked time in two minutes.
Canonical: https://easerix.com/docs/croncrunch/quickstart
Create a client, create a project under it with an hourly rate, then start
the timer on that project and stop it when you're done. The entry lands in
Time entries, the Timesheet, the Calendar, and Reports automatically — one
recording, every view.
## Steps
1. Open **CronCrunch** in the Easerix app ([app.easerix.com](https://app.easerix.com)).
2. **Add a client** (optional but recommended): go to **Clients**, choose **New client**, name it, pick a color, and **Create**.
3. **Create a project**: go to **Projects**, choose **New project**, and fill in:
| Field | Notes |
|---|---|
| Name | e.g. "Brand redesign" |
| Client | pick one, or "No client" |
| Hourly rate ($) | used for billable amounts in reports |
| Budget (hours, optional) | shows a progress bar on the project card |
| Color | how the project appears everywhere |
4. **Start tracking**: go to **Timer**, pick the project, describe what you're doing, and hit **Start timer**. The elapsed clock runs front and center.
5. **Stop timer** when you're done. The entry appears in the **Today** list under the timer.
## Prefer logging after the fact?
On **Time entries**, use the quick-add bar: pick a project, describe the work, type a duration, and hit **Log**. Durations are forgiving — `1:30`, `90`, and `1.5` all mean ninety minutes.
## Verify it works
Open **Reports** — your time shows under **Total tracked**, attributed to the project (and its billable amount if the project has a rate). The **Timesheet** and **Calendar** show the same entry in their week views.
## Good to know
- The timer needs at least one project to exist — that's why the project comes first.
- Entries are editable after the fact: description, project, duration, billable flag, and tags.
- A forgotten timer that runs unusually long can trigger a reminder notification so it doesn't quietly eat your day.
# Timesheets, tags, budgets, and reports
The week grid, the drag-to-create calendar, entry tags, project budgets, and reading and exporting reports.
Canonical: https://easerix.com/docs/croncrunch/timesheets-and-reports
CronCrunch gives you three ways to work a week — a timesheet grid where you
type hours per project per day, a calendar where you drag to log time, and
the entries log itself. Tags slice entries across projects, budgets track
project burn, and Reports totals it all with CSV export.
## The timesheet: type your week
**Timesheet** shows a week as a grid — one row per project, one column per day, cells in `h:mm`.
1. Click any cell and type a duration (`1:30`, `90`, or `1.5` all work), then press **Enter**.
2. Typing *more* than the cell holds logs the difference as a new entry that day; typing *less* trims the most recent entries down (deleting emptied ones).
3. Use **Add project row…** to bring a project without entries into the grid.
4. Arrows move between weeks; **This week** jumps back to now. Row, column, and week totals update as you go.
Whether the week starts on Monday or Sunday is yours to set in **Settings → Preferences**.
## The calendar: drag your week
**Calendar** shows the same week as a timeline — one column per day, hours down the side, a line marking "now".
- **Drag on empty space** to draw an entry; it snaps to 15 minutes and shows the time range while you drag. Release, pick the project, describe it, and **Log time**.
- **Click an entry** to edit its project and description, or delete it.
- Overlapping entries sit side by side instead of hiding each other.
## Tags
Tags label entries across projects — "deep work", "meetings", "support".
- Edit any entry on **Time entries** and toggle tag chips on it; type into **+ new tag** to create one on the spot.
- Filter the whole log by tag with the **All tags** dropdown.
- Tags ride along into the CSV export.
## Project budgets
Give a project a **Budget (hours)** when creating or editing it, and its card on **Projects** shows tracked-vs-budget with a progress bar — e.g. *12h of 40h · 30%*. Go past the budget and the bar turns red with an **Over budget** label. Budgets are informational: nothing blocks you from logging past them.
## Reports
**Reports** answers "where did the hours go, and what did they earn":
| Element | What it shows |
|---|---|
| Range tabs | This week, Last week, This month, Last month, All time, Custom |
| **Total tracked** | all hours in the range |
| **Billable** | hours on billable entries |
| **Amount** | billable hours × each project's hourly rate, in your currency |
| **By project / client** | share of time per project or per client, with bars |
| **By day** | a bar per day, quiet days shown as gaps |
**Export CSV** downloads every entry in the range — date, times, project, client, description, tags, duration, hours, and billable flag — ready for invoicing or a spreadsheet.
### Rounding
**Settings → Preferences → Rounding** rounds durations on reports to the nearest 5–30 minutes, *display only* — entries always keep their exact time, and the report says so when rounding is on.
## Frequently asked questions
### Do the timesheet, calendar, and entries log show different data?
No — they're three views of the same entries. Log time in any of them and it appears in all of them, and in Reports.
### What does sharing a project do?
**Share** on a project card makes it visible to your organization so teammates can log *their own* hours against it. It never exposes your entries — time entries are always personal. **Make private** reverses it.
### Which currency do amounts use?
The one you pick in **Settings → Preferences → Currency** (USD, EUR, GBP, CAD, AUD, NGN). It changes display only — rates stay as entered.
# CLI
The easerix command line — tasks and links from your terminal, with device-code sign-in or an API key for CI.
Canonical: https://easerix.com/docs/developers/cli
`easerix` is a command-line client for your Easerix workspace: sign in once
with a device code, then list and create tasks and short links from the
terminal, with JSON output for scripting. In CI and other headless
environments, set `EASERIX_API_KEY` instead of signing in.
## Availability
The CLI currently ships to teams through GitHub releases on the private
Easerix repository — there's no public Homebrew tap or npm package yet. If
your team has repository access, install with an authenticated
[GitHub CLI](https://cli.github.com):
```bash
bash <(gh api repos/perizerlabs/easerix/contents/cli/install.sh --jq '.content' | base64 -d)
```
This downloads the latest release binary for your OS/architecture and installs
it to `/usr/local/bin` (override with `EASERIX_INSTALL_DIR`).
## Sign in
```bash
easerix login
```
The CLI uses a device-code flow — the same experience as `gh auth login`, so
it works over SSH and inside containers. It prints a code like
`ESRX-XXXX-XXXX`, opens the approval page in your browser (use `--no-browser`
to just print the URL), and finishes once you approve. Approval grants the CLI
**one workspace**; to switch workspaces, run `easerix login` again.
`easerix logout` signs out on this machine only. To revoke the connection
entirely: **account.easerix.com → Connected apps**.
Credentials are stored in your OS keychain when available, otherwise in
`~/.config/easerix/` (set `EASERIX_NO_KEYRING=1` to force file storage —
`easerix context` shows which backend is active).
## Commands
| Command | What it does |
|---|---|
| `easerix login [--no-browser]` | Sign in with a device code |
| `easerix logout` | Sign out on this machine |
| `easerix whoami` | Who you're signed in as, workspace, role, tools |
| `easerix context` | Active configuration and where each value came from |
| `easerix orgs list` | List every workspace you belong to |
| `easerix tasks teams` | List the workspace's teams |
| `easerix tasks issues [--team ]` | List issues, optionally one team's |
| `easerix tasks create [--team ]` | Create an issue |
| `easerix links list` | List your short links |
| `easerix links create [--title ]` | Create a tracked short link |
Every listing command takes `-o table` (default), `-o json`, or `-o plain`.
Exit codes: `0` success, `1` error, `2` cancelled, `4` authentication needed.
## Headless use: `EASERIX_API_KEY`
Set `EASERIX_API_KEY` to an [API key](/developers/overview#api-keys) and the
CLI skips the stored session entirely — it exchanges the key for a short-lived
access token automatically and re-exchanges as needed. Nothing is written to
disk, which makes it the right mode for CI:
```bash
EASERIX_API_KEY=esrx_o_... easerix links create https://example.com/launch -o json
```
Other environment variables:
| Variable | Purpose |
|---|---|
| `EASERIX_API_KEY` | Headless credential — takes precedence over any stored session |
| `EASERIX_NO_KEYRING` | Store tokens in files instead of the OS keychain |
| `EASERIX_AUTH_URL` | Override the auth server (defaults to `https://auth.easerix.com`) |
## Frequently asked questions
### Can I use the CLI in a container or over SSH?
Yes — that's what the device-code login is for. Run `easerix login
--no-browser`, open the printed URL on any machine, and enter the code. Or
skip login entirely with `EASERIX_API_KEY`.
### How do I script against the output?
Pass `-o json` and pipe to `jq`. `-o plain` gives tab-separated values for
shell tools.
### Which tools does the CLI cover?
Tasks and Links today, plus account commands (`whoami`, `orgs`, `context`).
For everything else, use the [REST APIs](/developers/api) directly.
# MCP server
Connect Claude and other AI clients to your Easerix workspace at mcp.easerix.com.
Canonical: https://easerix.com/docs/developers/mcp
Easerix runs a hosted MCP server at **mcp.easerix.com**. Add it to Claude or
any MCP-compliant AI client, approve it once in your browser, and the AI can
act on your workspace — create issues and short links, pull analytics, check
documents, log CRM activity — limited to the tools your workspace has enabled.
MCP (Model Context Protocol) is an open standard that lets AI assistants call
tools on external services — the Easerix MCP server is how an AI client gets
hands on your workspace.
## Add it to Claude
**claude.ai / Claude Desktop** — Settings → Connectors → Add custom connector,
with the URL:
```
https://mcp.easerix.com/mcp
```
**Claude Code** — one command:
```bash
claude mcp add --transport http easerix https://mcp.easerix.com/mcp
```
Any other MCP client that supports streamable HTTP and OAuth works the same
way — point it at `https://mcp.easerix.com/mcp`.
## Signing in happens automatically
The server is an OAuth 2.1 resource server, and compliant clients handle the
whole flow on their own: on first use the client discovers
`auth.easerix.com`, registers itself, and opens your browser to approve the
connection. You pick **one workspace** to grant — everything the AI does is
scoped to it. To switch workspaces, disconnect and connect again. Revoke a
connection anytime under **account.easerix.com → Connected apps**.
## Available tools
Tools are gated by workspace access: a tool group only appears if that Easerix
tool is enabled for the granted workspace. `whoami` is always available and
tells the AI who it's acting as, in which workspace, with which tools.
### Tasks
| Tool | What it does |
|---|---|
| `tasks_list_teams` | List the workspace's Tasks teams |
| `tasks_list_issues` | List issues, optionally for one team |
| `tasks_create_issue` | Create an issue in a team |
### Links
| Tool | What it does |
|---|---|
| `links_create_short_link` | Create a tracked `esrx.ly` short link |
| `links_list` | List short links with click counts |
| `links_update_link` | Change destination, title, tags, pause, or archive |
| `links_delete_link` | Delete a link and its click history |
| `links_link_stats` | Per-link analytics: clicks, countries, devices, referrers, conversions |
| `links_batch` | Pause, activate, archive, or delete up to 100 links at once |
| `links_list_bio_pages` | List link-in-bio pages |
| `links_update_bio_page` | Edit a bio page's content or publish state |
| `links_analytics_summary` | Account-wide link analytics |
### Sign
| Tool | What it does |
|---|---|
| `sign_list_documents` | List documents with signature status |
### CronCrunch
| Tool | What it does |
|---|---|
| `croncrunch_time_summary` | Tracked-time summary by project and by day |
### CRM
| Tool | What it does |
|---|---|
| `crm_pipeline_summary` | Pipeline stages with deal counts and value |
| `crm_create_contact` | Create a contact |
| `crm_create_company` | Create a company |
| `crm_create_deal` | Open a deal in the default pipeline |
| `crm_move_deal` | Move a deal to another stage |
| `crm_log_activity` | Log a note, call, email, or meeting |
### Notes
| Tool | What it does |
|---|---|
| `notes_search` | Full-text search your pages (titles + content) |
| `notes_read_page` | Read a page's content as plain text |
| `notes_create_page` | Create a page from Markdown |
| `notes_append_to_page` | Append Markdown to an existing page |
### Forms
| Tool | What it does |
|---|---|
| `forms_list_forms` | List your forms |
| `forms_form_summary` | A form's settings, latest submissions, and 14-day series |
The list grows as tools gain MCP surface — reconnect (or restart your client)
to pick up new ones.
## Frequently asked questions
### Can the AI reach workspaces I didn't grant?
No. The connection is scoped to the single workspace you approved. Data in
other workspaces is invisible to it.
### Does the AI get more access than I have?
No — it acts as you, with your role, and only in tools enabled for the
workspace.
### My client asks for a client ID or secret — what do I enter?
Nothing. The server supports dynamic client registration, so compliant clients
register themselves. There is no shared client secret.
# Developer overview
Base URLs, API-key and OAuth 2.1 authentication, and the error shape shared by every Easerix API.
Canonical: https://easerix.com/docs/developers/overview
Every Easerix tool exposes a REST API, most of them under one domain at
`api.easerix.com/`. You authenticate by exchanging an API key for a
short-lived access token at `auth.easerix.com`, then send that token as a
Bearer header. Every error response is a single JSON field: `{"error": "..."}`.
## Base URLs
| API | Base URL | Notes |
|---|---|---|
| Auth & accounts | `https://auth.easerix.com` | Token exchange, OAuth 2.1, workspaces |
| Tasks | `https://api.easerix.com/tasks` | |
| Notes | `https://api.easerix.com/notes` | |
| Sign | `https://api.easerix.com/sign` | |
| Links | `https://api.easerix.com/links` | Management API — short links themselves redirect on `esrx.ly` |
| Forms | `https://f.easerix.com` | API and public form endpoints share the form domain |
| CronCrunch | `https://api.easerix.com/croncrunch` | |
| CRM | `https://api.easerix.com/crm` | |
| Notifications | `https://notifications-api.easerix.com` | |
| MCP | `https://mcp.easerix.com` | [MCP server](/developers/mcp) for AI clients |
Older per-tool hosts of the form `-api.easerix.com` are retired — use the
bases above. Endpoints are versioned under `/v1/`, so a full URL looks like
`https://api.easerix.com/links/v1/links`.
## Authentication
Easerix has two ways in: **API keys** for scripts, integrations, and CI, and
**OAuth 2.1** for apps that act on behalf of a user (this is what the
[MCP server](/developers/mcp) and the [CLI](/developers/cli) use).
### API keys
Create keys at **account.easerix.com**:
- **Personal keys** (`esrx_u_...`) — under **API keys**. The key acts as you,
in one workspace you pick when creating it.
- **Workspace keys** (`esrx_o_...`) — under your organization's **API keys**
page (requires settings permission, team workspaces only). These are service
accounts: they act as a role you choose — `member`, `billing`, or `admin`,
never `owner`.
Either kind can be restricted to specific tools and given an expiry (1 year by
default, 90 days, or never). The full key is shown **once** at creation — store
it in a secret manager. Revoke any key from the same page.
### Using a key: exchange it for an access token
API keys are never sent to the tool APIs directly. Exchange the key for a
short-lived access token first:
```bash
curl -s https://auth.easerix.com/v1/auth/token \
-H "Content-Type: application/json" \
-d '{"api_key": "esrx_u_..."}'
```
```json
{
"access_token": "",
"expires_at": "2026-08-09T18:04:05Z",
"org": { "id": "...", "name": "...", "role": "member", "tools": ["links", "tasks"] }
}
```
There is no refresh token — when `expires_at` passes, exchange the key again.
Then call any tool API with the token:
```bash
curl -s https://api.easerix.com/links/v1/links \
-H "Authorization: Bearer $ACCESS_TOKEN"
```
Sending the raw `esrx_...` key as a Bearer token to a tool API returns `401`.
### OAuth 2.1
`auth.easerix.com` is a standards-compliant OAuth 2.1 authorization server:
| Endpoint | Purpose |
|---|---|
| `GET /.well-known/oauth-authorization-server` | Discovery metadata (RFC 8414) |
| `POST /oauth/register` | Dynamic client registration (RFC 7591) — public PKCE clients |
| `GET /oauth/authorize` | Authorization code flow — PKCE `S256` required |
| `POST /oauth/token` | Token endpoint — authorization code, refresh token, and device code grants |
| `POST /oauth/device/authorization` | Device authorization (RFC 8628) for terminals and headless devices |
Registered clients are public (no client secret) and must use PKCE with
`S256`. When a user approves your app they grant it **one workspace**; the
tokens you receive are scoped to that workspace and to the tools enabled
there. Users can revoke a grant anytime under **account.easerix.com →
Connected apps**.
MCP-compliant AI clients drive this whole flow automatically — see
[MCP server](/developers/mcp).
## Errors
Every non-2xx response has the same body — one human-readable field:
```json
{ "error": "missing bearer token" }
```
Status codes follow convention: `400` for invalid input, `401` for missing or
expired credentials, `403` for a role or permission you don't have, `404` for
anything that doesn't exist. Note that resources outside your workspace also
read as `404`, never `403` — the API doesn't confirm the existence of things
you can't access.
## Workspace scoping
Every resource belongs to the workspace your token was issued for. A token
carries the workspace, your role in it, and the tools enabled there; a request
to a tool that isn't enabled for the workspace is rejected. To work across two
workspaces, create a key (or grant) per workspace.
## Next steps
- [Browse the API reference](/developers/api) — every endpoint, generated from the OpenAPI specs
- [Connect an AI client over MCP](/developers/mcp)
- [Receive webhooks](/developers/webhooks)
## Frequently asked questions
### How long do access tokens last?
They're short-lived. Don't hardcode a duration — read `expires_at` from the
exchange response and re-exchange your API key when it passes.
### Can I use one API key across all my workspaces?
No. A key is bound to a single workspace at creation. Create one key per
workspace you need to automate.
### Where do I find the OpenAPI specs?
Each API's raw spec is linked from the [API reference](/developers/api) — for
example [links.v1.yaml](/openapi/links). They're standard OpenAPI 3 YAML,
ready for client generators.
# Webhooks
Signed outbound webhooks from Links and Sign, and email delivery for Forms submissions.
Canonical: https://easerix.com/docs/developers/webhooks
Links and Sign can POST JSON to your HTTPS endpoint when events happen — link
clicks and conversions, document signatures and completions. Every delivery is
signed with HMAC-SHA256 in an `X-Easerix-Signature` header so you can verify
it came from Easerix. Forms delivers submissions to email destinations today.
## Registering a webhook
Links and Sign share the same registration API:
```bash
curl -s https://api.easerix.com/links/v1/webhooks \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/hooks/easerix", "events": ["link.created", "link.clicked"]}'
```
The response includes the webhook and a signing `secret` (`whsec_...`) —
shown **once**, so store it immediately. The URL must be HTTPS. Manage
webhooks with `GET /v1/webhooks`, `PATCH /v1/webhooks/:id` (change `url`,
`events`, or `active`), and `DELETE /v1/webhooks/:id`.
| | Links | Sign |
|---|---|---|
| Base URL | `https://api.easerix.com/links` | `https://api.easerix.com/sign` |
| Default events | `link.created`, `link.updated`, `link.deleted` | `*` (all events) |
| Wildcard `*` subscription | No | Yes |
| Test delivery | `POST /v1/webhooks/:id/test` sends a `ping` | Not available |
| Webhooks per account | Unlimited | 10 |
## Verifying signatures
Every delivery carries these headers:
| Header | Value |
|---|---|
| `X-Easerix-Event` | The event name, e.g. `link.clicked` |
| `X-Easerix-Signature` | `sha256=` |
| `X-Easerix-Delivery` | Unique delivery ID, stable across retries (Links only) |
Compute the HMAC of the **raw request body** with your `whsec_...` secret and
compare:
```js
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, header, secret) {
const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
return timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}
```
Reject anything that doesn't verify. On Links, use `X-Easerix-Delivery` to
deduplicate retried deliveries.
## Links events
The payload envelope is always:
```json
{ "event": "link.clicked", "at": "2026-08-09T17:30:00Z", "data": { ... } }
```
| Event | `data` contains |
|---|---|
| `link.created` / `link.updated` / `link.deleted` | `{id, shortCode, destinationUrl, title, active, archived}` |
| `link.clicked` | `{link: {id, shortCode}, clicks, totalClicks}` |
| `link.converted` | `{link: {id, shortCode}, conversion: {id, name, amountCents, currency}, clickId, clickedAt, convertedAt}` |
`link.clicked` is **aggregated**: rapid clicks on the same link are batched
into one event with a `clicks` count, so don't assume one event per click —
`totalClicks` is the link's running total.
## Sign events
Sign payloads are flat (no `data` wrapper):
```json
{
"event": "document.signed",
"documentId": "doc_...",
"document": { "id": "doc_...", "name": "MSA — Acme", "status": "in_progress" },
"occurredAt": "2026-08-09T17:30:00Z",
"recipient": { "id": "...", "name": "...", "email": "...", "role": "signer", "status": "signed" }
}
```
| Event | Fires when |
|---|---|
| `document.sent` | A document is sent for signature |
| `document.viewed` | A recipient opens the document |
| `document.signed` | A recipient signs |
| `document.declined` | A recipient declines |
| `document.completed` | Everyone has signed |
| `document.voided` | The sender voids the document |
| `document.expired` | The document passes its expiry |
`recipient` is present only on the recipient-scoped events (`viewed`,
`signed`, `declined`).
## Delivery and retries
Both tools deliver with a 10-second timeout and treat any 2xx as success. A
failed delivery is retried twice (Links waits ~1s then ~5s; Sign waits 5s then
20s). After **20 consecutive failures** a webhook is automatically disabled —
re-enable it with `PATCH /v1/webhooks/:id {"active": true}`, which also resets
the failure count. Respond quickly (queue the work, return `200` immediately)
to stay under the timeout.
## Forms: email destinations
Forms doesn't send outbound webhooks yet — **email is the delivery channel**.
Each form has destinations managed at
`https://f.easerix.com/v1/forms/:id/destinations`; an `email` destination with
config `{"to": ["ops@example.com"]}` receives a branded notification for every
non-spam submission, with `Reply-To` set to the submitter when their email is
detected. Forms can also send an autoresponder to the submitter if you enable
it on the form.
To get form submissions into your own systems today, poll
`GET /v1/forms/:id/submissions` on the [Forms API](/developers/api/forms).
## Frequently asked questions
### Are deliveries replayed in order?
No ordering guarantee — treat each event independently and use the payload's
timestamp (`at` / `occurredAt`) rather than arrival order.
### My endpoint was down — did I lose events?
Each delivery is retried twice, then dropped. After 20 consecutive failures
the webhook is disabled entirely, so re-enable it and reconcile via the REST
API after an outage.
### Can I rotate the signing secret?
Delete the webhook and create a new one — a fresh secret is generated and
returned once on creation.
# Email notifications
Deliver each form submission to one or more inboxes, and send an automatic reply to the person who submitted.
Canonical: https://easerix.com/docs/forms/destinations
Each form delivers submissions by email: add an email destination with one or
more recipients, or use the simpler Target emails setting. Notification emails
include every submitted field with Reply-To set to the submitter, and an
optional autoresponder replies to the person who filled in the form. Spam is
never delivered.
## Add an email destination
1. Open the form in **Forms** ([app.easerix.com/forms](https://app.easerix.com/forms)) and scroll to **Where submissions go**.
2. On the **Email** card, choose **Add email destination**.
3. Enter one or more addresses in **Recipients** (comma-separated) and **Save**.
4. Every new non-spam submission is now emailed to those recipients.
Each destination has a toggle to pause it without deleting it, and a trash button to remove it. A form can have several email destinations with different recipient lists.
## The quick alternative: Target emails
Under **Settings → Notifications**, the **Target emails** field is a simpler way to say who gets emailed — it's also what the **Notify (emails)** field on the New form panel fills in. Target emails are used **only while the form has no enabled email destination**; once you add one, destinations take over.
## What the notification email contains
- Subject: **New submission: [form name]**.
- Every submitted field, listed key by key.
- **Reply-To** set to the submitter's email address when one is present (the `email` field, or the first value that looks like an email) — so you can reply straight from your mail client.
- A link to open the form in the Easerix app.
Submissions also land in your Easerix notification feed (the bell in the app, and the mobile inbox), independent of email.
## Autoresponder
Under **Settings → Autoresponder** you can send an automatic reply to the person who submitted:
1. Toggle **Enabled**.
2. Set a **Subject** (e.g. *We got your message*) and a **Body**.
3. Save. The reply goes to the submitter's email address whenever the submission contains one.
## What is never delivered
- **Spam** — submissions flagged by any [spam mechanism](/forms/spam) trigger no notification and no autoresponder.
- **Borderline submissions under AI review** — delivery waits for Claude's verdict and only goes out on a clean result, usually within seconds.
## More destinations
The app lists **Webhook, Slack, Google Sheets, Zapier, and CRM** on the destinations panel — all marked **Coming soon**. Email is the delivered channel today.
## Frequently asked questions
### Can I send submissions to multiple people?
Yes — put several comma-separated addresses in one destination's **Recipients**, or create multiple email destinations.
### Why didn't I get an email for a submission?
Check three things: the submission wasn't flagged as spam (toggle **Show spam** in the inbox), the email destination is enabled (not paused), and — if the AI filter is on — the verdict wasn't still pending when you looked. Marking a spam submission as not-spam afterwards does not re-send the email.
### Can I reply directly to the person who submitted?
Yes. Notification emails set **Reply-To** to the submitter's address whenever the submission includes one, so hitting Reply in your mail client goes to them, not to Forms.
# Forms
A form backend for any website — point your HTML form at an endpoint on f.easerix.com and manage submissions, spam, and email notifications.
Canonical: https://easerix.com/docs/forms
Easerix Forms is a form backend: point any HTML form or `fetch()` call at a
per-form endpoint on **f.easerix.com** and submissions land in an inbox with
spam filtering, email notifications, and CSV export. No server code needed —
Easerix's own contact form at easerix.com/contact runs on it.
## What you can do
| Capability | In short |
|---|---|
| Endpoints | One POST URL per form on `f.easerix.com` — plain HTML, `fetch()`, or any HTTP client |
| Inbox | Submissions per form and across all forms, searchable, with CSV export |
| Spam protection | Honeypot field, content filter, AI filter (Claude), Cloudflare Turnstile |
| Email notifications | Deliver each submission to one or more inboxes |
| Autoresponder | Automatic reply to the person who submitted |
| Success behavior | JSON response or a redirect to your thank-you page |
| Allowed origins | Restrict which sites may POST to a form (CORS) |
| Projects | Group forms by website, each with a name and color |
| Sharing | Share a form with your organization — teammates can read it and its inbox |
| Pause | Paused forms stop accepting submissions; existing data is kept |
## How it fits together
1. **Projects** group forms — typically one project per website.
2. **Forms** are endpoints. Creating a form generates a short unique URL like `https://f.easerix.com/a3kx9p`.
3. **Submissions** arrive at the endpoint, pass through the spam pipeline, and land in the form's inbox.
4. **Destinations** deliver clean submissions onward — email today, more channels marked *coming soon* in the app.
## Next steps
- [Collect your first submission](/forms/quickstart)
- [Set up spam protection](/forms/spam)
- [Deliver submissions by email](/forms/destinations)
- [Endpoint reference for developers](/forms/reference)
## Frequently asked questions
### Do I need my own backend?
No. The endpoint is the backend: your static site, landing page, or app POSTs directly to `f.easerix.com` and Forms stores, filters, and delivers the submission.
### Does Easerix use Forms itself?
Yes — the contact form on [easerix.com/contact](https://easerix.com/contact) submits to an Easerix Forms endpoint.
### Can visitors upload files?
File contents are not stored. If a submission includes file uploads, only the filenames are recorded, in a `_files` field on the submission.
### What happens when I pause a form?
The endpoint stops accepting submissions and responds as if the form didn't exist. Your collected submissions are kept, and reactivating the form brings the same endpoint URL back.
### What happens when I delete a form?
Deleting a form removes the endpoint **and all of its submissions**. The app asks you to confirm before doing it.
# Collect your first submission
Create a form, point an HTML form at its endpoint, and watch the submission land in your inbox.
Canonical: https://easerix.com/docs/forms/quickstart
Create a form in the Forms app to get a unique endpoint URL on f.easerix.com,
point your HTML form's `action` at it with `method="POST"`, and submit. The
submission appears in the form's inbox moments later, and everyone on the
notify list gets an email.
## Steps
1. Open **Forms** in the Easerix app ([app.easerix.com/forms](https://app.easerix.com/forms)).
2. Choose **New form**. Give it a **Form name** (e.g. *Contact — mysite.com*), optionally add **Notify (emails)**, pick a **Project**, and hit **Create form**.
3. On the form's page, copy the **Endpoint** URL from the banner at the top — it looks like `https://f.easerix.com/a3kx9p`. The **Integrate** panel below it has ready-made **HTML** and **fetch()** snippets for the same endpoint.
4. Put a form on your page that POSTs to the endpoint:
```html
```
5. Submit the form. The submission appears in the **Submissions** section of the form's page (and in the cross-form **Inbox**), and any notify emails go out.
## Verify it works
Fill in the form and submit. Standard form-encoded POSTs work as-is — no JavaScript, no API key. Within seconds the submission shows up under **Submissions** with every field you sent.
If it doesn't appear, click **Show spam** — a test that trips a spam rule is kept there rather than dropped. In particular, if **Cloudflare Turnstile** is switched on in **Settings → Spam protection** but your page doesn't render the Turnstile widget, submissions are flagged as spam; switch it off unless you've embedded the widget.
## Good to know
- New forms respond to a plain HTML post with a small JSON body (`{"ok": true}`). For a real site, set **Settings → Success behavior** to **Redirect** and point it at your thank-you page — visitors get sent there after submitting.
- Keep the hidden `_gotcha` field: it's the [honeypot](/forms/spam) that silently filters naive bots. It never appears in your inbox.
- Name your email field `email` — Forms uses it as the notification's Reply-To and as the [autoresponder](/forms/destinations) recipient.
- Submitting with JavaScript instead? See the [`fetch()` example in the endpoint reference](/forms/reference).
# Endpoint reference
The Forms ingest endpoint contract — accepted content types, field handling, responses, redirects, errors, CORS, and rate limits.
Canonical: https://easerix.com/docs/forms/reference
Send an HTTP POST to `https://f.easerix.com/[form-id]` with form-encoded,
multipart, or JSON data — no authentication. JSON clients get back
`{"ok": true, "id": "..."}`; plain HTML posts get a 302 redirect when the form
is configured for one. Requests are rate-limited to 60 per minute per form
and IP.
## The endpoint
Every form has one public URL, shown on its page in the app:
```
https://f.easerix.com/
```
| Method | Behavior |
|---|---|
| `POST` | Submit the form — the contract below |
| `OPTIONS` | CORS preflight — `204` with the form's CORS headers |
| `GET` | A friendly JSON hint that the endpoint accepts POST |
No API key, no auth header — the form id in the URL is the only credential. A paused or deleted form responds `404` to POST, indistinguishable from a form that never existed.
## Accepted content types
| Content-Type | Notes |
|---|---|
| `application/x-www-form-urlencoded` | What a plain HTML `