API reference
API reference
How to call the API with a workspace token: requests, permissions, limits, filters, errors, and every operation.
Everything a workspace token can do: 77 operations. The same list is behind the MCP server and the command line tool, so all three always match. A machine-readable copy is at /api/v1/openapi.json.
Making a request #
Every operation is a POST to https://clearsaga.com/api/v1/<name> with a JSON object as its body, reads included. No body means an empty object. The answer is a JSON object with status 200.
Create a token in the dashboard under Settings, Agent access, and send it in the Authorization header. That header is the only place a token is read from: never put one in a web address.
curl -s -X POST https://clearsaga.com/api/v1/sites.list \
-H "Authorization: Bearer clearsaga_pat_..." \
-H "Content-Type: application/json" \
-d '{}'Start with sites.list: every operation about one site needs a siteId, and the ids come from there. Times in answers are milliseconds since 1970 UTC. Dates you send are YYYY-MM-DD in the time zone of the site.
Tokens #
A token belongs to one workspace and has one or both of two permissions.
- Read: list the sites of the workspace and read every report, revenue included.
- Write: Create, change and delete funnels, campaign links, conversion goals, chart notes, saved segments and alert rules. Create and change store costs. Includes Read.
A token stops working when it is revoked, and when the person who created it leaves the workspace, is removed or becomes a Viewer. The API remembers a token for up to 60 seconds, so a change like that can take that long to reach it. "Last used" on the Agent access page is approximate: it is written at most every 5 minutes.
Every change made with a token is recorded with the name of the token and shown under Settings, Agent access. Reads are not recorded.
A delete removes for good and cannot be undone. It is recorded with the name of what was removed. Only the definition goes, and with an alert rule its delivery log: no recorded visit, event or order is touched.
What a token can never do #
- See or change another workspace. A token belongs to exactly one.
- Use a share link or the public demo in place of a site id.
- Export CSV files.
- Create, list or revoke tokens.
- Invite, remove or change members.
- Change the plan or anything about billing.
- Create, rename, reset or delete a site.
- Change tracking options, exclusions, revenue settings or notification settings.
- Enable, change or revoke share links or embed widgets.
- Rename or delete a preset conversion goal.
- Record an order.
- Connect a store or replace its secret.
- Read anything that names a person. Visitor profiles, where a site switched them on, are a random id with a generated name, places, devices and pages.
Dashboard actions that are closed to tokens, and why:
| Action | Why |
|---|---|
| Get install status | Install screen helper; returns the last hostname and the drop counters; sites.get says whether hits arrive. |
| Get snippet | Install screen helper; the text embeds the site key. |
| Create site | Counts against the plan and issues the tracking key. |
| Update site | Changes the domain the tracker is bound to. |
| Rotate site key | Breaks every installed snippet. |
| Update tracking options | Changes what the tracker collects on the live website. |
| Update ad attribution | Changes what the tracker reads on the live website and whether orders keep a customer key: a choice made in the dashboard. |
| Reset site data, Delete site | Destroys data. |
| Get site settings extras, Get visitor settings | Settings screen helper. |
| Set preset goal | Switching a preset on changes what the tracker sends. |
| Update revenue settings, Save attribution choice, Remove exclusion, Set revenue excluded paths | A site setting, changed in the dashboard. |
| Update workspace | A workspace setting, changed in the dashboard. |
| Delete workspace | Destroys everything the workspace holds. |
| Get billing summary | Per-person preference inside; plan facts reach tokens through usage.get. |
| Choose plan, Cancel plan | Changes the plan. Billing is done in the dashboard. |
| List exclusions | Can hold the team's IP addresses. |
| Add exclusion | Drops hits for good. |
| Get notification settings, Save notification settings | Holds email addresses. |
| Get sharing | Returns the dashboard share token. |
| Enable share link, Enable embed | Issues a public token. |
| Save share link, Regenerate share link, Revoke share link, Save embed | Changes a public share link or widget. |
| Regenerate embed, Disable embed | Breaks widgets already placed. |
| List site team, Invite viewer, Resend invite, Revoke invite, Remove viewer, List members, List workspace invitations, Invite admin, Change member role, Remove member | Lists or changes the people of the workspace. |
| Leave site, Leave workspace, Accept invite | About the signed-in person, and a token is not one. |
| List api tokens, Create api token, Revoke api token, List audit log | Tokens manage tokens only from the dashboard. |
| Update profile, Request email change, Change password, List sessions, Revoke session, Delete account | Belongs to the signed-in person's own account. |
| Get export estimate | Belongs with the CSV download, which is a file. |
| Update visitor settings | Sets a cookie on the live website and decides how consent is read: a choice with legal weight, made in the dashboard. |
| Find visitor by cookie | Takes a cookie value a visitor sent in a request; never through a token. |
| Delete visitor, Delete all visitors | Destroys data; tokens never delete. |
| Connect shopify | Opens the address that writes a store's orders to the site. |
| Set shopify secret | Saves or replaces the store secret that signs deliveries. |
| Disconnect shopify | Closes the address that receives orders. |
| Delete cost line | Destroys a cost line; profit changes for every past period. |
| Delete product cost | Destroys a product cost. |
Limits #
- 120 requests per minute per workspace, over all its tokens, counted approximately and per Cloudflare location. Over the limit the answer is 429 with
Retry-After: 60. - 30 attempts per minute from one address with tokens that are not recognised.
- A body of at most 65,536 bytes.
- Lists:
limit1 to 100,offset0 to 1,000, andoffsetpluslimitat most 1,000. A cursor is thenextCursorof the page before, sent back unchanged. - No CORS headers are sent. The API is for programs, not for pages in a browser.
Filters #
Reports about one site take a filters object such as { "country": "us", "path.has": "pricing" }. A plain key means "is", a key ending in .not means "is not" and a key ending in .has means "contains". Filters are joined with AND. At most 10 keys, of which at most 3 may end in .has. A key that is not in this table is refused.
| Key | Meaning |
|---|---|
path |
Page path is this value, for example /pricing. Applies to single page views, not to whole visits. |
path.not |
Page is not this value. Rows that have no value are kept. |
path.has |
Page contains this text, ignoring case. 1 to 100 characters. |
entry |
The first page of the visit is this path. |
entry.not |
Entry page is not this value. Rows that have no value are kept. |
entry.has |
Entry page contains this text, ignoring case. 1 to 100 characters. |
exit |
The last page of the visit is this path. |
exit.not |
Exit page is not this value. Rows that have no value are kept. |
exit.has |
Exit page contains this text, ignoring case. 1 to 100 characters. |
hostname |
The hostname the page was served from is this value, for example blog.example.com. |
hostname.not |
Hostname is not this value. Rows that have no value are kept. |
hostname.has |
Hostname contains this text, ignoring case. 1 to 100 characters. |
ref |
The referring site is this host, for example news.ycombinator.com. |
ref.not |
Referrer is not this value. Rows that have no value are kept. |
ref.has |
Referrer contains this text, ignoring case. 1 to 100 characters. |
channel |
The traffic channel is this one: direct, organic_search, organic_social, referral, ai, email, paid or campaign. |
channel.not |
Channel is not this value. Rows that have no value are kept. |
ai |
The visit came from this AI tool, by its key, for example chatgpt. |
ai.not |
AI tool is not this value. Rows that have no value are kept. |
country |
Two-letter country code in lower case, for example us. |
country.not |
Country is not this value. Rows that have no value are kept. |
region |
ISO 3166-2 region code, for example US-CA. Needs country; ignored without it. |
region.not |
Region is not this value. Rows that have no value are kept. |
city |
City name as shown in the locations breakdown. Needs country; ignored without it. |
city.not |
City is not this value. Rows that have no value are kept. |
city.has |
City contains this text, ignoring case. 1 to 100 characters. |
browser |
Browser name, for example Chrome. |
browser.not |
Browser is not this value. Rows that have no value are kept. |
browser.has |
Browser contains this text, ignoring case. 1 to 100 characters. |
os |
Operating system name, for example iOS. |
os.not |
OS is not this value. Rows that have no value are kept. |
os.has |
OS contains this text, ignoring case. 1 to 100 characters. |
device |
Device class: desktop, mobile or tablet. |
device.not |
Device is not this value. Rows that have no value are kept. |
utm_source |
The utm_source tag of the visit. |
utm_source.not |
Source is not this value. Rows that have no value are kept. |
utm_source.has |
Source contains this text, ignoring case. 1 to 100 characters. |
utm_medium |
The utm_medium tag of the visit. |
utm_medium.not |
Medium is not this value. Rows that have no value are kept. |
utm_medium.has |
Medium contains this text, ignoring case. 1 to 100 characters. |
utm_campaign |
The utm_campaign tag of the visit. |
utm_campaign.not |
Campaign is not this value. Rows that have no value are kept. |
utm_campaign.has |
Campaign contains this text, ignoring case. 1 to 100 characters. |
utm_term |
The utm_term tag of the visit. |
utm_term.not |
Term is not this value. Rows that have no value are kept. |
utm_term.has |
Term contains this text, ignoring case. 1 to 100 characters. |
utm_content |
The utm_content tag of the visit. |
utm_content.not |
Content is not this value. Rows that have no value are kept. |
utm_content.has |
Content contains this text, ignoring case. 1 to 100 characters. |
conversion |
Only visitors who completed this conversion goal, by the goal's name from goals.list. A name that matches no enabled goal is ignored. |
event |
Only hits of this custom event, by name. |
prop |
Only custom events with this property, written key:value. |
Errors #
Every error is { "error": { "code", "message" } }, sometimes with detail, fields or reason. Decide on code, which is stable. message is one sentence that says what to change.
| Status | Code | When | Also carries |
|---|---|---|---|
| 400 | invalid_input |
The body is not a JSON object, fails the input schema of the operation, or holds a field the operation does not know. | fields: each refused field with its path and the reason. |
| 400 | invalid |
A value passed the schema and was then refused by the operation. | detail: the field. |
| 400 | invalid_cursor |
A cursor the server did not issue. | |
| 401 | unauthenticated |
The Authorization header is missing or malformed, or the token is unknown or revoked, or the person who created it is no longer an Owner or Admin. | Header WWW-Authenticate: Bearer. |
| 402 | workspace_locked |
The workspace's dashboards are locked, so reports answer no numbers. | reason: limit, grace or expired. The message says what lifts the lock. |
| 403 | insufficient_scope |
An operation that changes something was called with a token that can only read. | Header WWW-Authenticate with scope="write". |
| 404 | not_found |
The path is not an operation, the site is not in the workspace of the token, or a row with that id does not exist. | detail: what was missing, for a row. |
| 405 | method_not_allowed |
An operation was called with a method other than POST. | Header Allow. |
| 409 | taken |
A name or key is already in use. | detail: which. |
| 409 | limit_reached |
A cap on how many of something a site or workspace may hold. | detail: which. |
| 409 | feature_off |
The operation is about remembered visitors and the site has Remember returning visitors switched off. | detail: returning_visitors. The message says where an owner or admin switches it on. |
| 413 | payload_too_large |
The body is larger than 65,536 bytes. | |
| 415 | unsupported_media_type |
A body was sent with a content type other than application/json. | |
| 422 | too_much_data |
The answer would need more data than one request may read. Narrow the range or add a filter. | |
| 429 | rate_limited |
More than 120 requests in a minute for the workspace, or more than 30 tokens in a minute from one address that were not recognised. | Header Retry-After: 60. |
| 500 | internal |
Something went wrong on our side. |
When the workspace is locked #
The dashboards of a workspace lock when its monthly allowance is used up, and when its trial or plan has ended. While they are locked, every operation that reads a report answers 402 with the code workspace_locked and a reason of limit, grace or expired. The message says what lifts the lock.
Operations that list definitions, such as funnels, goals or saved segments, keep answering, and so do the operations that change them. The page of each read operation says which of the two it does. Plans, usage and limits explains the lock itself.
All operations #
| Operation | Kind | What it does |
|---|---|---|
sites.get |
Read | Site details |
sites.list |
Read | List sites |
overview.bots |
Read | Filtered bot traffic |
overview.breakdown |
Read | Breakdown by dimension |
overview.conversionPeak |
Read | Conversions by hour |
overview.kpis |
Read | Headline numbers |
overview.missingPages |
Read | Pages not found |
overview.timeseries |
Read | Traffic over time |
events.counts |
Read | Custom event counts |
events.insights |
Read | Custom event trends |
events.log |
Read | Activity log |
events.names |
Read | Custom event names |
events.properties |
Read | Custom event properties |
realtime.snapshot |
Read | Realtime snapshot |
realtime.visitor |
Read | Live visitor |
visitors.get |
Read | Remembered visitor |
visitors.list |
Read | List remembered visitors |
visitors.summary |
Read | Returning visitors summary |
funnels.create |
Write | Create funnel |
funnels.delete |
Write | Delete funnel |
funnels.duplicate |
Write | Duplicate funnel |
funnels.list |
Read | List funnels |
funnels.report |
Read | Funnel report |
funnels.update |
Write | Update funnel |
campaigns.breakdown |
Read | Campaign breakdown |
campaigns.conversions |
Read | Campaign conversions |
campaigns.create |
Write | Create campaign link |
campaigns.delete |
Write | Delete campaign link |
campaigns.list |
Read | List campaign links |
campaigns.totals |
Read | Campaign totals |
campaigns.update |
Write | Update campaign link |
goals.breakdown |
Read | Conversion rate breakdown |
goals.completions |
Read | Goal completions |
goals.create |
Write | Create conversion goal |
goals.delete |
Write | Delete conversion goal |
goals.list |
Read | List conversion goals |
goals.update |
Write | Update conversion goal |
revenue.kpis |
Read | Revenue totals |
revenue.refunds |
Read | Refunds list |
revenue.report |
Read | Revenue attribution report |
revenue.timeseries |
Read | Revenue over time |
performance.report |
Read | Web vitals report |
store.attribution |
Read | Which ads earned sales |
store.costs |
Read | Store costs |
store.createCostLine |
Write | Add a cost line |
store.creatives |
Read | Creative report |
store.customers |
Read | New customers |
store.orders |
Read | Orders and their channels |
store.overlap |
Read | Channel overlap |
store.profit |
Read | Profit report |
store.reconciliation |
Read | Revenue reconciliation |
store.saveCostSettings |
Write | Save cost rules |
store.setProductCosts |
Write | Set product costs |
store.summary |
Read | Store summary |
store.updateCostLine |
Write | Change a cost line |
store.uploadProductCosts |
Write | Upload product costs |
store.uploadSpend |
Write | Upload ad spend |
notes.create |
Write | Create chart note |
notes.delete |
Write | Delete chart note |
notes.list |
Read | List chart notes |
notes.update |
Write | Update chart note |
segments.create |
Write | Save a segment |
segments.delete |
Write | Delete a segment |
segments.list |
Read | List saved segments |
segments.update |
Write | Update a segment |
alerts.create |
Write | Create alert rule |
alerts.delete |
Write | Delete alert rule |
alerts.deliveries |
Read | List alert deliveries |
alerts.list |
Read | List alert rules |
alerts.test |
Write | Send test delivery |
alerts.update |
Write | Update alert rule |
embeds.get |
Read | Embed widgets |
workspace.get |
Read | Workspace details |
workspace.live |
Read | Visitors online now |
workspace.summary |
Read | All sites summary |
usage.get |
Read | Usage this month |
connections.list |
Read | Ad platform connections |